home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / BROWSER.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-09-06  |  80.5 KB  |  3,151 lines

  1. #ifdef TEST
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <time.h>
  5. #define NULLCHAR ((char *)0)
  6. #define NULLFILE ((FILE *)0)
  7. #define callocw calloc
  8. #define SCREENwidth 80
  9. char *strlwr(char *s);
  10. void rip(register char *s);
  11. void kwait (void *);
  12. #define BROWSER 1
  13. #else
  14. #include "global.h"
  15. #include "socket.h"
  16. #include "netuser.h"
  17. #include "commands.h"
  18. #include "session.h"
  19. #include "files.h"
  20. #ifdef MSDOS
  21. #include <conio.h>
  22. #include "ctype.h"
  23. #endif
  24. #endif
  25. #include "browser.h"
  26.  
  27.  
  28. #if !defined(_lint) && !defined(TEST)
  29. static char rcsid[] OPTIONAL = "$Id: browser.c,v 1.12 1997/09/07 00:31:16 root Exp root $";
  30. #endif
  31.  
  32. #ifdef BROWSER
  33. struct parseparams {
  34.     int    title_input;
  35.     int    titleindex;
  36.     char    title[128];
  37.     int    marquee_input;
  38.     int    marqueeindex;
  39.     char    marquee[1024];
  40.     char    buf[512];
  41.     int    inword;
  42.     int    index;
  43.     int    lastwhite;
  44.     int    eat_semicolon;
  45.     int    preformatted;
  46.     int    nowrap;
  47.     int    center;
  48.     int    center_one;
  49.     int    listlevel;
  50.     int    listcount[10];
  51.     int    listtype[10];
  52. };
  53.  
  54.  
  55. #define OP_UNKNOWN    -1
  56. #define OP_A        1
  57. #define OP_BR        2
  58. #define OP_CAPTION    3
  59. #define OP_CENTER    4
  60. #define OP_CONSUME    5
  61. #define OP_DD        6
  62. #define OP_DL        7
  63. #define OP_DT        8
  64. #define OP_FORM        9
  65. #define OP_FRAME    10
  66. #define OP_HEADER    11
  67. #define OP_H1        12
  68. #define OP_HR        13
  69. #define OP_IGNORE    14
  70. #define OP_IMAGE    15
  71. #define OP_INPUT    16
  72. #define OP_ISINDEX    17
  73. #define OP_LI        18
  74. #define OP_MARQUEE    19
  75. #define OP_OL        20
  76. #define OP_P        21
  77. #define OP_PRE        22
  78. #define OP_TABLE    23
  79. #define OP_TD        24
  80. #define OP_TH        25
  81. #define OP_TITLE    26
  82. #define OP_TR        27
  83. #define OP_UL        28
  84.  
  85.  
  86. static struct opcodes    {
  87.     const char *opcode;
  88.     int value;
  89. } supported_opcodes[] = {
  90.     { "!--",    OP_IGNORE },    /* not a real tag, used by HTML comments */
  91.     { "a",         OP_A },
  92.     { "br",        OP_BR },
  93.     { "caption",    OP_CAPTION },
  94.     { "center",    OP_CENTER },
  95.     { "comment",    OP_CONSUME },
  96.     { "dd",        OP_DD },
  97.     { "dl",        OP_DL },
  98.     { "dt",        OP_DT },
  99.     { "dir",    OP_UL },
  100.     { "form",    OP_FORM },
  101.     { "frame",    OP_FRAME },
  102.     { "h1",        OP_H1 },
  103.     { "h2",        OP_HEADER },
  104.     { "h3",        OP_HEADER },
  105.     { "h4",        OP_HEADER },
  106.     { "h5",        OP_HEADER },
  107.     { "h6",        OP_HEADER },
  108.     { "hr",        OP_HR },
  109.     { "img",    OP_IMAGE },
  110.      { "input",    OP_INPUT },
  111.     { "isindex",    OP_ISINDEX },
  112.     { "li",        OP_LI },
  113.     { "marquee",    OP_MARQUEE },
  114.     { "menu",    OP_UL },
  115.     { "ol",        OP_OL },
  116.     { "p",         OP_P },
  117.     { "pre",    OP_PRE },
  118.     { "script",    OP_CONSUME },
  119.     { "table",    OP_TABLE },
  120.     { "td",        OP_TD },
  121.     { "th",        OP_TH },
  122.     { "title",    OP_TITLE },
  123.     { "tr",        OP_TR },
  124.     { "ul",        OP_UL },
  125.     { NULLCHAR,    OP_UNKNOWN }
  126. };
  127.  
  128.  
  129. #define INPUT_UNKNOWN    -1
  130. #define INPUT_CHECKBOX    1
  131. #define INPUT_HIDDEN    2
  132. #define INPUT_IMAGE    3
  133. #define INPUT_PASSWORD    4
  134. #define INPUT_RADIO    5
  135. #define INPUT_RESET    6
  136. #define INPUT_SUBMIT    7
  137. #define INPUT_TEXT    8
  138.  
  139.  
  140. static struct opcodes supported_inputs[] = {
  141.     { "checkbox",    INPUT_CHECKBOX },
  142.     { "hidden",    INPUT_HIDDEN },
  143.     { "image",    INPUT_IMAGE },
  144.     { "password",     INPUT_PASSWORD },
  145.     { "radio",    INPUT_RADIO },
  146.     { "reset",    INPUT_RESET },
  147.     { "submit",    INPUT_SUBMIT },
  148.     { "text",    INPUT_TEXT },
  149.     { NULLCHAR,    INPUT_UNKNOWN }
  150. };
  151.  
  152.  
  153. #define NBSP    300    /* special 'char' value for a non-breakable space */
  154. #define SECS_IN_6MONTHS 15768000 /* seconds in 1/2 year */
  155.  
  156. static char HLINE[] = "--------------------------------------------------------------------------";
  157.  
  158. static const char *HEADLINENewsHost = "www.lantz.com";
  159. static const char *HEADLINENewsUrl = "/tnos/headline_news.html";
  160.  
  161.  
  162. static void scroll_marquee (struct browser *br);
  163. static int gethead (const char *url, char *location, char *modified, char *content);
  164. static char *rebuild_url (struct browser *br, char *cp);
  165. static char *get_tag_data (struct browser *br, FILE *fp, char **cur_url, struct tag **cur_tag);
  166. static void parse_and_format_table (struct browser *br, struct parseparams *p, FILE *fp, int border);
  167. static void trim_trailing_white (char *str);
  168. static int disect_url (char *url, char *type, char *host, int *port, char *component);
  169. static char *build_url (struct browser *br, char *type, char *host, int port, char *component);
  170. static void parse_url (struct browser *br, FILE *fp, int headersexist);
  171. static int opcode_lookup (struct opcodes *op, char *opcode);
  172. static void add_char (struct parseparams *p, struct browser *br, int c);
  173. static void break_line (struct browser *br, struct parseparams *p);
  174. static void carryover_check (struct browser *br, struct parseparams *p);
  175. static char *alloc_string (struct browser *br, const char *str, int centerit);
  176. #ifndef TEST
  177. static int http_connect (char const *host, int port, struct sockaddr_in *fsocket);
  178. static void share_cookies (int s, char const *host, char const *component);
  179. static void add_cookie (char *host, char *path, time_t expires, char *name, char *value);
  180. static int get_url (struct browser *br, const char *url, const char *post, int postmode, char *enctype, int storeit);
  181. static void blank_status (struct browser *br);
  182. static void send_blank_lines (int count);
  183. static int http_url_to_file (struct browser *br, char *refered, char const *host, int port, char const *component, char *auth, FILE *fp, const char *post, int postmode, char *enctype, int quiet);
  184. static void clear_browser (struct browser *br);
  185. static void render_screen (struct browser *br);
  186. static void spawn_telnet (int port, void *host, void *p);
  187. static void spawn_ftp (int unused, void *host, void *file);
  188. static void send_mailto (char *addr);
  189. static void goto_stat (struct browser *br, int plusoffset);
  190. static void add_string (struct parseparams *p, struct browser *br, const char *str);
  191. static void end_link (struct parseparams *p, struct browser *br, int c);
  192. static void start_link (struct parseparams *p, struct browser *br, char *url, struct formlink *fl, int c);
  193. static char *encode_post (char *str);
  194.  
  195. extern unsigned char SCREENlength, SCREENwidth;
  196. extern char shortversion[];
  197. extern char *strToBase64 (char *str);
  198. extern struct cmds Cmds[];
  199.  
  200. static char *BrowserEmail = NULLCHAR;
  201.  
  202.  
  203. #define STATUS_LINES    3
  204.  
  205. #ifdef MSDOS
  206. #define SCREENLENGTH (SCREENlength + 1)
  207. #else
  208. #define SCREENLENGTH (SCREENlength)
  209. #endif
  210.  
  211. #endif
  212.  
  213.  
  214.  
  215. static int
  216. opcode_lookup (struct opcodes *op, char *opcode)
  217. {
  218.     for ( ; op->opcode; op++)    {
  219.         if (!strcasecmp (op->opcode, opcode))
  220.             return op->value;
  221.     }
  222.     return OP_UNKNOWN;
  223. }
  224.  
  225.  
  226.  
  227. #ifndef TEST
  228.  
  229. /* fetch the specified URL
  230.    returns -1 if cannot retrieve URL, 0 if no error, 1 if spawned other handler */
  231. static int
  232. get_url (struct browser *br, const char *url, const char *post, int postmode, char *enctype, int storeit)
  233. {
  234. FILE *fp;
  235. char type[12], host[100], component[512];
  236. int port, k;
  237. int retval = 0;
  238. char *tmp;
  239. char *refered = NULLCHAR;
  240.  
  241. redirection:
  242.  
  243.     if (url == NULLCHAR)
  244.         return -1;
  245.  
  246. #if 1
  247.     br->savingonly = 0;
  248.     if (gethead (url, NULLCHAR, NULLCHAR, component) == 1)    {
  249.         /* exists (and is http), so check content type here */
  250.         tmp = skipwhite (component);
  251.         if (strncasecmp (tmp, "text/", 5))    {
  252.             /* not text, so prompt for whether to get it */
  253.             blank_status (br);
  254.             Current->ttystate.edit = Current->ttystate.echo = 1;
  255.             goto_stat (br, 0);
  256.             cputs ("This URL cannot be displayed: ");
  257.             cputs (url);
  258.             cputs (Eol);
  259.             cputs ("       Save it to disk? ");
  260.  
  261.             kwait (NULL);
  262.             (void) recvline (Curproc->input, (unsigned char *) host, 100);
  263.             rip (host);
  264.  
  265.             Current->ttystate.edit = Current->ttystate.echo = 0;
  266.             if (tolower(*host) != 'y')
  267.                 return -1;
  268.             br->savingonly = 1;
  269.         }
  270.     }
  271. #endif
  272.  
  273.     fp = tmpfile ();
  274.     if (fp == NULLFILE)
  275.         return -1;    /* shouldn't happen */
  276.         
  277.     tmp = strdup (url);
  278.     if (disect_url (tmp, type, host, &port, component))
  279.         return -1;
  280.     if (!*host)    {
  281.         strncpy (host, component, 100);
  282.         strcpy (component, "/");
  283.     }
  284.     free (tmp);
  285.  
  286.     if (storeit)    {
  287.         /* add this link to the linkback buffer */
  288.         if (br->current_linkback == (LINKBACKS - 1))    {
  289.             /* roll one off the top */
  290.             free (br->linkback[0]);
  291.             for (k = 1; k < LINKBACKS; k++)
  292.                 br->linkback[k - 1] = br->linkback[k];
  293.             br->total_in_linkback--;
  294.             br->current_linkback--;
  295.         }
  296.         if ((br->current_linkback || br->total_in_linkback) && br->current_linkback != (br->total_in_linkback - 1))    {
  297.             /* overwritting a previous entry - clear all that follow */
  298.             for (k = br->current_linkback + 1; k < br->total_in_linkback; k++)    {
  299.                 free (br->linkback[k]);
  300.                 br->linkback[k] = NULLCHAR;
  301.             }
  302.             br->total_in_linkback = br->current_linkback + 1;
  303.         }
  304.         br->linkback[br->total_in_linkback++] = strdup (url);
  305.         br->current_linkback = br->total_in_linkback - 1;
  306.     }
  307.  
  308.  
  309.     if (!strcasecmp (type, "telnet"))    {
  310.         (void) newproc ("tel_browser", 1024, spawn_telnet, port, host, NULL, 0);
  311.         (void) kwait (NULL);
  312.         return 1;
  313.     } else if (!strcasecmp (type, "ftp"))    {
  314.         (void) newproc ("ftp_browser", 1024, spawn_ftp, 0, host, component, 0);
  315.         (void) kwait (NULL);
  316.         return 1;
  317.     } else if (!strcasecmp (type, "mailto"))    {
  318.         send_mailto (component);
  319.         return 1;
  320.     } else if (!strcasecmp (type, "file"))    {
  321.         fp = fopen (component, "r");
  322.         if (fp != NULLFILE)    {
  323.             clear_browser (br);
  324.             br->current_url = build_url (br, type, host, port, component);
  325.             parse_url (br, fp, 0);
  326.             (void) fclose (fp);
  327.             return 0;
  328.         }
  329.         return -1;
  330.     } else if (strcasecmp (type, "http"))    {
  331.         /* don't do anything, for now */
  332.         return 1;
  333.     }
  334.  
  335.     refered = strdup (br->current_url);
  336.     clear_browser (br);
  337.     br->current_url = build_url (br, type, host, port, component);
  338.  
  339.     br->host = strdup (host);
  340.     br->port = port;
  341.  
  342.     /* connect to the remote HTTP server, placing data into the tempfile */
  343.     if ((retval = http_url_to_file (br, refered, host, port, component, br->auth, fp, post, postmode, enctype, 0)) == 0)
  344.         parse_url (br, fp, 1);
  345.  
  346.     (void) fclose (fp);
  347.     free (refered);
  348.  
  349.     /* check to see if there was a redirection to a new URL */
  350.     if (br->redirect)    {
  351.         if (storeit)    {
  352.             /* back up the backlink, to make this invisible */
  353.             br->current_linkback--;
  354.             br->total_in_linkback--;
  355.         }
  356.         url = br->redirect;
  357.         goto redirection;
  358.     }
  359.  
  360.     /* check to see if there was an authorization error */
  361.     if (br->auth)    {
  362.         /* Try again with the desired authorization info */
  363.         if (storeit)    {
  364.             /* back up the backlink, to make this invisible */
  365.             br->current_linkback--;
  366.             br->total_in_linkback--;
  367.         }
  368.         url = br->current_url;
  369.         goto redirection;
  370.     }
  371.  
  372.     return retval;
  373. }
  374. #endif
  375.  
  376.  
  377.  
  378. /* build a URL from type, host, port, and component and current host info*/
  379. static char *
  380. build_url (struct browser *br, char *type, char *host, int port, char *component)
  381. {
  382. char buf[512], portstr[16], namebuf[128];
  383. char *usehost;
  384.  
  385.     if (port != 80)
  386.         sprintf (portstr, ":%d", port);
  387.     else
  388.         portstr[0] = 0;
  389.  
  390.     if (!strcasecmp (type, "mailto"))
  391.         sprintf (buf, "%s:%s", type, component);
  392.     else if (!strcasecmp (type, "file"))
  393.         sprintf (buf, "%s:/%s", type, component);
  394.     else    {
  395.         usehost = (*host) ? host : br->host;
  396.         sprintf (namebuf, "%s:/%s%s", type, (*usehost == '/') ? "" : "/",
  397.             usehost);
  398.         if (namebuf[strlen(namebuf) - 1] == '/')
  399.             namebuf[strlen(namebuf) - 1] = 0;
  400.         sprintf (buf, "%s%s%s%s", namebuf, portstr,
  401.             (component[0] == '/') ? "" : "/", component);
  402.     }
  403.     return strdup (buf);
  404. }
  405.  
  406.  
  407.  
  408. /* disect the URL named into type, host, port, and component */
  409. /* component will NOT start with a '/' for relative URL's */
  410. static int
  411. disect_url (char *url, char *type, char *host, int *port, char *component)
  412. {
  413. char *cp;
  414.  
  415.     host[0] = ' ';
  416.     if (!strncasecmp (url, "mailto:", 7))    {
  417.         /* special case for mailto's */
  418.         strcpy (component, &url[7]);
  419.         strcpy (type, "mailto");
  420.         return 0;
  421.     }
  422.  
  423.     if (!strncasecmp (url, "file:", 5))    {
  424.         cp = &url[5];
  425.         if (*cp == '/' && cp[1] == '/')
  426.             cp++;
  427.         strcpy (component, cp);
  428.         strcpy (type, "file");
  429.         return 0;
  430.     }
  431.  
  432.     if (strchr (url, ':') && strncasecmp (url, "ftp:", 4) &&
  433.         strncasecmp (url, "telnet:", 7) && strncasecmp (url, "http:", 5))
  434.             return -1;
  435.  
  436.     host[0] = 0;
  437.     *port = 80;
  438.     strcpy (type, "http");
  439.     strcpy (component, "/");
  440.  
  441.     /* first we look for urls with a scheme name */
  442.     if ((cp = strstr (url, ":/")) != NULLCHAR)    {
  443.         *cp++ = 0;
  444.         strcpy (type, url);
  445.         url = cp;
  446.     }
  447.  
  448.     /* next we look to see if this is a network path */
  449.     if (!strncmp (url, "//", 2))    {
  450.         url += 2;
  451.         cp = strpbrk (url, ":/?;");
  452.         if (cp)    {
  453.             /* there is more to this address */
  454.             if (*cp == ':')    {    /* contains a port number */
  455.                 *cp++ = 0;
  456.                 strcpy (host, url);
  457.                 url = cp;
  458.                 cp = strchr (url, '/');
  459.                 if (cp)    {
  460.                     *cp++ = 0;
  461.                     strcpy (&component[1], cp);
  462.                 }
  463.                 *port = atoi (url);
  464.                 return 0;
  465.             }
  466.             /* otherwise, no port number */
  467.             *cp++ = 0;
  468.             strcpy (host, url);
  469.             strcpy (&component[1], cp);
  470.             return 0;
  471.         }
  472.         strcpy (host, url);
  473.         return 0;
  474.     }
  475.  
  476.     /* otherwise, it is either an absolute or relative path on this host */
  477.     strcpy (component, url);
  478.     return 0;
  479. }
  480.  
  481.  
  482.  
  483.  
  484. #ifndef TEST
  485.  
  486. static void
  487. clear_browser (struct browser *br)
  488. {
  489. int k;
  490. struct formlink *fl, *last;
  491.  
  492.     /* first we cleanup any previous url info */
  493.     for (k = 0; k < br->lines_in_url; k++)    {
  494.         free (br->display_lines[k]);
  495.         br->display_lines[k] = NULLCHAR;
  496.     }
  497.  
  498.     free (br->current_url);
  499.     br->current_url = NULLCHAR;
  500.  
  501.     free (br->redirect);
  502.     br->redirect = NULLCHAR;
  503.  
  504.     free (br->host);
  505.     br->host = NULLCHAR;
  506.  
  507.     free (br->title);
  508.     br->title = NULLCHAR;
  509.  
  510.     free (br->marquee);
  511.     br->marquee = NULLCHAR;
  512.     br->marquee_position = 0;
  513.  
  514.     br->has_an_index = br->lines_in_url = br->current_top_line = 0;
  515.  
  516.     for (k = 0; k < br->link_count; k++)    {
  517.         free (br->links[k].url);
  518.         br->links[k].url = NULLCHAR;
  519.         br->links[k].startline = br->links[k].endline = 0;
  520.         br->links[k].startcolumn = br->links[k].endcolumn = 0;
  521.         br->links[k].flink = NULLFLINK;
  522.         br->links_per_line[k] = 0;
  523.     }
  524.  
  525.     br->consume = br->status = br->link_count = br->current_link = 0;
  526.  
  527.     if (br->form != NULLFORM)    {
  528.         free (br->form->action);
  529.         free (br->form->enctype);
  530.  
  531.         for (fl = br->form->flink; fl != NULLFLINK; fl = last)    {
  532.             last = fl->next;
  533.             free (fl->data);
  534.             free (fl->name);
  535.             free (fl->value);
  536.             free (fl->align);
  537.             free (fl);
  538.         }
  539.         free (br->form);
  540.         br->form = NULLFORM;
  541.     }
  542. }
  543.  
  544.  
  545.  
  546. static int
  547. http_connect (char const *host, int port, struct sockaddr_in *fsocket)
  548. {
  549. int s;
  550.  
  551.     fsocket->sin_family = AF_INET;
  552.     fsocket->sin_port = (int16) port;
  553.  
  554.     if ((fsocket->sin_addr.s_addr = resolve (host)) == 0)
  555.         return -1;
  556.  
  557.     if ((s = socket (AF_INET, SOCK_STREAM, 0)) == -1)
  558.         return -1;
  559.  
  560.     (void) sockmode (s, SOCK_ASCII);
  561.     if (connect (s, (char *) fsocket, SOCKSIZE) == -1)
  562.         return -1;
  563.     
  564.     return s;
  565. }
  566.  
  567.  
  568.  
  569. /* connects to remote HTTP server, and saves URL data
  570.    returns -1 if cannot retrieve URL, 0 if no error */
  571. static int
  572. http_url_to_file (struct browser *br, char *refered, char const *host, int port, char const *component, char *auth, FILE *fp, const char *post, int postmode, char *enctype, int quiet)
  573. {
  574. struct sockaddr_in fsocket;
  575. int s, c;
  576. char *saved, *cp;
  577. int incomingcount = 0;
  578. char buf[20];
  579. int endofline = 0;
  580.  
  581.     /* telnet to 'host', port 'port', and send a request for 'component' */
  582.     /* return -1 if can't connect or http error */
  583.  
  584.     s = http_connect (host, port, &fsocket);
  585.     if (s == -1)
  586.         return s;
  587.  
  588.     if (!quiet)    {
  589.         blank_status (br);
  590.         goto_stat (br, 1);
  591.         cputs ("Opening - http://");
  592.         cputs (host);
  593.         cputs (component);
  594.         cputs ("...");
  595.     }
  596.  
  597.     saved = strdup (component);
  598.     if ((cp = strchr (saved, '#')) != NULLCHAR)
  599.         *cp = 0;
  600.  
  601.     if (!quiet)    {
  602.         goto_stat (br, 2);
  603.         cputs ("Sending request...");
  604.         rflush ();
  605.     }
  606.  
  607.     /* send the request to the server */
  608.     usprintf (s, "%s %s%s%s HTTP/1.0\nHost: %s\nUser-Agent: %s\n",
  609.         (post != NULLCHAR && postmode == 0) ? "POST" : "GET",
  610.         saved, (post != NULLCHAR && postmode == 1) ? "?" : "",
  611.         (post != NULLCHAR && postmode == 1) ? post : "",
  612.         Hostname, shortversion);
  613.     free (saved);
  614.  
  615. #if 0
  616.     usputs (s, "Accept: text/html, text/plain\n");
  617. #endif
  618.     usputs (s, "Accept-Language: en\nPragma: no-cache\n");
  619.     usprintf (s, "Cache-Control: no-cache\n");
  620.     if (BrowserEmail != NULLCHAR)
  621.         usprintf (s, "From: %s\n", BrowserEmail);
  622.     else
  623.         usprintf (s, "From: wwwuser@%s\n", Hostname);
  624.     if (refered && *refered)
  625.         usprintf (s, "Referer: %s\n", refered);
  626.     if (auth)
  627.         usprintf (s, "Authorization: Basic %s\n", auth);
  628.     if (post != NULLCHAR && postmode == 0)    {
  629.         if (enctype != NULLCHAR)    /* should always be non-null */
  630.             usprintf (s, "Content-type: %s\n", enctype);
  631.         usprintf (s, "Content-Length: %d\n", strlen (post) + 1);
  632.     }
  633. #ifndef TEST
  634.     share_cookies (s, host, component);
  635. #endif
  636.     
  637.     usputc (s, '\n');
  638.     if (post != NULLCHAR && postmode == 0)    {
  639.         usputs (s, post);
  640.         usputc (s, '\n');
  641.     }
  642.  
  643.     if (!quiet)    {
  644.         goto_stat (br, 2);
  645.         cputs ("Request sent...   ");
  646.         rflush ();
  647.     }
  648.  
  649.     kwait (NULL);
  650.     
  651.     /* then while connection still open, store data into 'fp' */
  652.     while ((c = recvchar (s)) != EOF)    {
  653.         if (br->savingonly && c == '\n')    {
  654.             if (endofline)
  655.                 (void) sockmode (s, SOCK_BINARY);
  656.             endofline = 1;
  657.         } else
  658.             endofline = 0;
  659.         fputc (c, fp);
  660.         incomingcount++;
  661.         if (!quiet)    {
  662.             if (!(incomingcount % 50))    {
  663.                 goto_stat (br, 2);
  664.                 cputs ("Read ");
  665.                 sprintf (buf, "%d", incomingcount);
  666.                 cputs (buf);
  667.                 cputs (" bytes...  ");
  668.                 rflush ();
  669.             }
  670.         }            
  671.     }
  672.  
  673.     close_s (s);
  674.     
  675.     if (!quiet)    {
  676.         goto_stat (br, 2);
  677.         cputs ("Read Complete...     ");
  678.         rflush ();
  679.     }
  680.  
  681.     /* and reset to file for the next routine */    
  682.     rewind (fp);
  683.     return 0;
  684. }
  685. #endif
  686.  
  687.  
  688.  
  689. /* this routine allocates a string, optionally centering it */
  690. static char *
  691. alloc_string (struct browser *br, const char *str, int centerit)
  692. {
  693. char *buf;
  694. int len, k;
  695. struct url_links *lnk;
  696.  
  697.     buf = (char *) mallocw (((strlen(str) > SCREENwidth) ? strlen(str) : SCREENwidth) + 2);
  698.     if (centerit)    {
  699.         len = (int) (SCREENwidth - strlen (str));
  700.         if (len < 0)
  701.             len = 0;
  702.         len /= 2;
  703.         sprintf (buf, "%*.*s%s", len, len, " ", str);
  704.  
  705.         /* We must also adjust any links on the current line */
  706.         for (lnk = br->links, k = 0; k < br->link_count; k++,lnk++)    {
  707.             if (lnk->startline == br->lines_in_url)
  708.                 lnk->startcolumn += len;
  709.             if (lnk->endline == br->lines_in_url)
  710.                 lnk->endcolumn += len;
  711.         }
  712.     } else
  713.         strcpy (buf, str);
  714.  
  715.     return buf;
  716. }
  717.  
  718.  
  719.  
  720. static void
  721. carryover_check (struct browser *br, struct parseparams *p)
  722. {
  723. char carryover[256];
  724. struct url_links *lnk;
  725. int length;
  726.  
  727.     if (br->lines_in_url >= MAX_URL_LINES || p->nowrap)
  728.         return;
  729.  
  730.     if (p->index > (SCREENwidth - 1))    {
  731.         if (p->lastwhite == 0)
  732.             return;
  733.         /* handle word wrap here */
  734.         p->buf[p->lastwhite] = p->buf[p->index] = 0;
  735.         br->display_lines[br->lines_in_url++] = alloc_string (br, p->buf, p->center);
  736.         strcpy (carryover, &p->buf[p->lastwhite + 1]);
  737.  
  738.         if (p->listlevel)
  739.             sprintf (p->buf, "%*.*s", (p->listlevel * 4) + 4,
  740.                 (p->listlevel * 4) + 4, " ");
  741.         else
  742.             p->buf[0] = 0;
  743.  
  744.         /* check if the last link needs adjusting */
  745.         lnk = &br->links[br->link_count - 1];
  746.         if (lnk->startline == (br->lines_in_url - 1) && lnk->startcolumn > p->lastwhite)    {
  747.             /* entire link on next line */
  748.             lnk->startline++;
  749.             lnk->endline++;
  750.             length = lnk->endcolumn - lnk->startcolumn;
  751.             if (p->listlevel)
  752.                 lnk->startcolumn = (p->listlevel * 4);
  753.             else
  754.                 lnk->startcolumn = 0;
  755.             lnk->endcolumn = lnk->startcolumn + length;
  756.             if (lnk->flink != NULLFLINK)    {
  757.                 lnk->flink->line = lnk->startline;
  758.                 lnk->flink->col = lnk->startcolumn + 1;
  759.             }
  760.             br->links_per_line[br->lines_in_url]++;
  761.             br->links_per_line[br->lines_in_url - 1]--;
  762.  
  763.         } else if (lnk->endline == (br->lines_in_url - 1) && lnk->endcolumn > p->lastwhite)    {
  764.             /* end of link needs adjusting */
  765.             lnk->endline++;
  766.             lnk->endcolumn = (int) (strlen (p->buf) - 1);
  767.             br->links_per_line[br->lines_in_url]++;
  768.         }
  769.  
  770.         strcat (p->buf, carryover);
  771.         p->index = (int) strlen (p->buf);
  772.         p->lastwhite = 0;
  773.     }
  774.  
  775. }
  776.  
  777.  
  778.  
  779. static void
  780. break_line (struct browser *br, struct parseparams *p)
  781. {
  782.     carryover_check (br, p);
  783.     p->buf[p->index] = 0;
  784.     if (p->index && (br->lines_in_url < MAX_URL_LINES))
  785.         br->display_lines[br->lines_in_url++] = alloc_string (br, p->buf, p->center + p->center_one);
  786.     p->index = p->lastwhite = p->inword = 0;
  787.     p->center_one = 0;
  788. }
  789.  
  790.  
  791.  
  792. static void
  793. start_link (struct parseparams *p, struct browser *br, char *url, struct formlink *fl, int c)
  794. {
  795.     if (br->link_count < MAX_URL_LINKS)    {
  796.         br->links[br->link_count].startline = br->lines_in_url;
  797.         br->links[br->link_count].url = url;
  798.         br->links[br->link_count].flink = fl;
  799.         br->links_per_line[br->lines_in_url]++;
  800.         if (c && p->index && p->buf[p->index - 1] != ' ')
  801.             add_char (p, br, ' ');
  802.         br->links[br->link_count].startcolumn = p->index;
  803.         if (c)
  804.             add_char (p, br, c);
  805.     }
  806.     p->inword = 0;
  807. }
  808.  
  809.  
  810.  
  811. static void
  812. end_link (struct parseparams *p, struct browser *br, int c)
  813. {
  814. char buf[2];
  815. int k;
  816.  
  817.     if (br->link_count < MAX_URL_LINKS)    {
  818.         if (c && p->index && p->buf[p->index - 1] == ' ')
  819.             p->index--;
  820.         if (!p->index)    {
  821.             /* adjust this back to the end of the last line */
  822.             strcpy (p->buf, br->display_lines[br->lines_in_url - 1]);
  823.             buf[0] = (char) c;
  824.             buf[1] = 0;
  825.             strcat (p->buf, buf);
  826.             free (br->display_lines[br->lines_in_url - 1]);
  827.             br->display_lines[br->lines_in_url - 1] = strdup (p->buf);
  828.             br->links[br->link_count].endline = br->lines_in_url - 1;
  829.             br->links[br->link_count].endcolumn = (int) (strlen (p->buf) - 1);
  830.         } else    {
  831.             if (c)
  832.                 add_char (p, br, c);
  833.             br->links[br->link_count].endline = br->lines_in_url;
  834.             br->links[br->link_count].endcolumn = p->index - 1;
  835.         }
  836.         for (k = br->links[br->link_count].startline + 1; k <= br->links[br->link_count].endline; k++)
  837.             br->links_per_line[k]++;
  838.         br->link_count++;
  839.     }
  840. }
  841.  
  842.  
  843.  
  844. static void
  845. add_string (struct parseparams *p, struct browser *br, const char *str)
  846. {
  847.     while (*str)
  848.         add_char (p, br, *str++);
  849. }
  850.  
  851.  
  852.  
  853. static void
  854. add_char (struct parseparams *p, struct browser *br, int c)
  855. {
  856.     if (br->consume)
  857.         return;
  858.  
  859.     if (c == ' ' || c == '\t' || c == '\n')    {
  860.         if (p->title_input)    {
  861.             p->title[p->titleindex++] = ' ';
  862.             return;
  863.         }
  864.         if (p->marquee_input)    {
  865.             p->marquee[p->marqueeindex++] = ' ';
  866.             return;
  867.         }
  868.         if (!p->preformatted && !p->inword)
  869.             return;    /* eat multiple white */
  870.         p->inword = 0;
  871.         if (p->index > (SCREENwidth - 1))
  872.             carryover_check (br, p);
  873.  
  874.         p->lastwhite = p->index;
  875.         p->buf[p->index++] = (p->preformatted) ? (char) c : ' ';
  876.         return;
  877.     }
  878.  
  879.     if (c == NBSP)
  880.         c = ' ';
  881.  
  882.     if (c == ';' && p->eat_semicolon)    {
  883.         p->eat_semicolon = 0;
  884.         return;
  885.     }
  886.     p->eat_semicolon = 0;
  887.     if (p->title_input)    {
  888.         p->title[p->titleindex++] = (char) c;
  889.         return;
  890.     }
  891.     if (p->marquee_input)    {
  892.         p->marquee[p->marqueeindex++] = (char) c;
  893.         return;
  894.     }
  895.     p->inword = 1;
  896.     if (!p->index && p->listlevel)    {
  897.         sprintf (p->buf, "%*.*s", (p->listlevel * 4) + 4,
  898.             (p->listlevel * 4) + 4, " ");
  899.         p->index = (int) strlen (p->buf);
  900.         p->lastwhite = p->index - 1;
  901.     }
  902.     p->buf[p->index++] = (char) c;
  903. }
  904.  
  905.  
  906.  
  907. /* cp is malloced, and gets freed. this returns a new malloced string */
  908. static char *
  909. rebuild_url (struct browser *br, char *cp)
  910. {
  911. char *saved, *temp, *cp3;
  912. char type[12], host[100], component[512];
  913. int port;
  914. int badurl = 0;
  915.  
  916.     badurl = disect_url (cp, type, host, &port, component);
  917.     free (cp);
  918.  
  919.     if (!badurl)    {
  920.  
  921.         if (*component != '/' && !strcasecmp (type, "http"))    {    /* relative URL */
  922.             saved = strdup (component);
  923.             temp = strdup (br->current_url);
  924.             (void) disect_url (temp, type, host, &port, component);
  925.             if ((cp3 = strrchr (component, '/')) != NULLCHAR)
  926.                 cp3++;
  927.             else
  928.                 cp3 = component;
  929.             strcpy (cp3, saved);
  930.             free (saved);
  931.             free (temp);
  932.         }
  933.  
  934.         cp = build_url (br, type, host, port, component);
  935.     } else
  936.         cp = NULLCHAR;
  937.     return cp;
  938. }
  939.  
  940.  
  941. /* parse the tempfile given (containing the raw URL data)
  942.    and place into the browser structure
  943.    currently bitbuckets all http header lines */
  944. static void
  945. parse_url (struct browser *br, FILE *fp, int headersexist)
  946. {
  947. struct parseparams *p;
  948. struct tag *tag;
  949. struct formlink *fl;
  950. char url[128];
  951. int headers_done = 0;
  952. int c, k;
  953. char host[100], component[512];
  954. char *cp;
  955. int linkfound = 0;
  956. char *realm = NULLCHAR;
  957. struct realms *rl;
  958. int badauth = 0;
  959. int saveinstead = 0;
  960. #ifndef TEST
  961. FILE *savefp = NULLFILE;
  962. #endif
  963.  
  964.     p = (struct parseparams *) callocw (1, sizeof (struct parseparams));
  965.  
  966.     if (headersexist)    {
  967.         /* first line contains request status */
  968.         (void) fgets (p->buf, 128, fp);
  969.         if ((cp = strchr (p->buf, ' ')) != NULLCHAR)
  970.             br->status = atoi (++cp);
  971.         else
  972.             br->status = 0;
  973.     } else
  974.         headers_done = 1;
  975.     
  976.     /* skip all http header lines */
  977.     while (!headers_done)    {
  978.         (void) fgets (p->buf, 128, fp);
  979.         rip (p->buf);
  980.         if (!*p->buf)
  981.             break;
  982.         if (!strncasecmp (p->buf, "Content-type: ", 14))    {
  983.             if (strncmp (&p->buf[14], "text", 4))
  984.                 /* if not a text URL, so we save it */
  985.                 saveinstead = 1;
  986.         }
  987.  
  988. #ifndef TEST
  989.         if (!strncasecmp (p->buf, "Set-Cookie: ", 12))    {
  990.             /* parse line, and call add_cookie here */
  991.             char *hostnm = NULLCHAR, *path = NULLCHAR, *name = NULLCHAR, *value = NULLCHAR;
  992.             char *tmp;
  993.             time_t expires = 0;
  994.  
  995.             cp = &p->buf[12];
  996.             while (cp && *cp)    {
  997.                 cp = skipwhite (cp);
  998.                 if (!strncasecmp (cp, "domain=", 7))    {
  999.                     cp += 7;
  1000.                     tmp = strchr (cp, ';');
  1001.                     if (tmp)
  1002.                         *tmp++ = 0;
  1003.                     hostnm = strdup (cp);
  1004.                     cp = tmp;
  1005.                 } else if (!strncasecmp (cp, "path=", 5))    {
  1006.                     cp += 5;
  1007.                     tmp = strchr (cp, ';');
  1008.                     if (tmp)
  1009.                         *tmp++ = 0;
  1010.                     path = strdup (cp);
  1011.                     cp = tmp;
  1012.                 } else if (!strncasecmp (cp, "expires=", 8))    {
  1013.                     /* ignore this, treat all expires as + 6 months */
  1014.                     tmp = strchr (cp, ';');
  1015.                     if (tmp)
  1016.                         cp = tmp;
  1017.                     else
  1018.                         cp = &cp[strlen(cp)];
  1019.                 } else if (!strncasecmp (cp, "secure", 6))    {
  1020.                     /* not supported, just skip it */
  1021.                     cp = skipnonwhite (cp);
  1022.                 } else if ((tmp = strchr (cp, '=')) != NULLCHAR)    {
  1023.                     /* this is the name=value string */
  1024.                     *tmp++ = 0;
  1025.                     name = strdup (cp);
  1026.                     cp = strchr (tmp, ';');
  1027.                     if (cp)
  1028.                         *cp++ = 0;
  1029.                     value = strdup (tmp);
  1030.                 }
  1031.                 if (cp)    {
  1032.                     cp = skipwhite (cp);
  1033.                     if (*cp == ';')
  1034.                         cp++;
  1035.                 }
  1036.             }
  1037.             if (path == NULLCHAR)    {
  1038.                 tmp = strchr (br->current_url, '/');
  1039.                 if (tmp)    {
  1040.                     tmp += 2;
  1041.                     tmp = strchr (tmp, '/');
  1042.                     path = strdup (tmp);
  1043.                 } else
  1044.                     path = strdup ("/");
  1045.             }
  1046.             if (hostnm == NULLCHAR)
  1047.                 hostnm = strdup (br->host);
  1048.             expires = time ((time_t *)0) + SECS_IN_6MONTHS;
  1049.             
  1050.             add_cookie (hostnm, path, expires, name, value);
  1051.             free (hostnm);
  1052.             free (path);
  1053.             free (name);
  1054.             free (value);
  1055.             continue;
  1056.         }
  1057.  
  1058.         /* if we are being redirected, then get the new URL */
  1059.         if (br->status == 302 && !strncasecmp (p->buf, "Location: ", 10))    {
  1060.             br->redirect = strdup (&p->buf[10]);
  1061.             return;
  1062.         }
  1063.  
  1064.         /* if we need authorization, then get it */
  1065.         if (br->status == 401 && !strncasecmp (p->buf, "WWW-Authenticate: ", 18))    {
  1066.             if ((cp = strstr (p->buf, "basic")) == NULLCHAR)
  1067.                 continue;    /* only basic auth supported */
  1068.             if ((cp = strstr (p->buf, "realm=\"")) != NULLCHAR)    {
  1069.                 realm = strdup (&cp[7]);
  1070.                 if ((cp = strchr (realm, '"')) != NULLCHAR)
  1071.                     *cp = 0;
  1072.             }
  1073.  
  1074.             /* first look to see if we already know how to answer this one */
  1075.             for (rl = br->myrealms; rl; rl = rl->next)    {
  1076.                 if (realm && !strcasecmp (realm, rl->realm))    {
  1077.                     if (br->auth && !strcmp (br->auth, rl->auth))    {
  1078.                         /* we already USED this authentication, and it failed! */
  1079.                         badauth = 1;
  1080.                         free (rl->auth);
  1081.                         rl->auth = NULLCHAR;
  1082.                         free (br->auth);
  1083.                         break;
  1084.                     }
  1085.                     free (realm);
  1086.                     free (br->auth);
  1087.                     br->auth = NULLCHAR;
  1088.                     br->auth = strdup (rl->auth);
  1089.                     return;
  1090.                 }
  1091.             }
  1092.  
  1093.             /* nope, this is a new one */
  1094.             blank_status (br);
  1095.             goto_stat (br, 0);
  1096.             sprintf (component, "Enter authentication information for %s at %s",
  1097.                 (realm) ? realm : "selected URL", br->host);
  1098.             cputs (component);
  1099.             Current->ttystate.edit = Current->ttystate.echo = 1;
  1100.             goto_stat (br, 1);
  1101.             cputs ("          User Name: ");
  1102.             kwait (NULL);
  1103.             (void) recvline (Curproc->input, (unsigned char *) url, 100);
  1104.             rip (url);
  1105.  
  1106.             Current->ttystate.echo = 0;
  1107.             if (*url)    {
  1108.                 goto_stat (br, 2);
  1109.                 cputs ("          Password: ");
  1110.                 kwait (NULL);
  1111.                 (void) recvline (Curproc->input, (unsigned char *) host, 100);
  1112.                 rip (host);
  1113.  
  1114.                 sprintf (component, "%s:%s", url, host);
  1115.                 br->auth = strToBase64 (component);
  1116.  
  1117.                 /* and add to my current realms */
  1118.                 if (!badauth)    {
  1119.                     rl = (struct realms *) callocw (1, sizeof (struct realms));
  1120.                     if (rl != NULLREALMS)    {
  1121.                         rl->next = br->myrealms;
  1122.                         br->myrealms = rl;
  1123.                         rl->host = strdup (br->host);
  1124.                         rl->realm = strdup (realm);
  1125.                     }
  1126.                 }
  1127.                 if (rl != NULLREALMS)
  1128.                     rl->auth = strdup (br->auth);
  1129.             }
  1130.  
  1131.             Current->ttystate.edit = 0;
  1132.             free (realm);
  1133.             if (*url)
  1134.                 return;
  1135.         }
  1136.  
  1137.  
  1138.         /* else, bitbucket any other headers, for now */
  1139. #endif
  1140.     }
  1141.  
  1142.     free (br->auth);
  1143.     br->auth = NULLCHAR;
  1144.  
  1145. #ifndef TEST
  1146.     if (saveinstead)    {
  1147.         cp = strrchr (br->current_url, '/');
  1148.         if (!cp)    /* it always WILL be nonzero - this is for lint */
  1149.             return;
  1150.         sprintf (component, "%s%s", IncomingURLs, cp);
  1151.         (void) mkdir (IncomingURLs, 0777);    /* create, just in case */
  1152.         savefp = fopen (component, WRITE_BINARY);
  1153.         if (savefp == NULLFILE)
  1154.             return;
  1155.     }
  1156. #endif
  1157.  
  1158.     kwait (NULL);
  1159.     /* the rest of the file is the data of the URL */
  1160.     while ((c = fgetc (fp)) != EOF)    {
  1161. #ifndef TEST
  1162.         if (savefp != NULLFILE)    {
  1163.             fputc (c, savefp);
  1164.             continue;
  1165.         }
  1166. #endif
  1167.         if (p->preformatted && c != '<')    {
  1168.             if (c == '\n')    {
  1169.                 c = p->index;
  1170.                 break_line (br, p);
  1171.                 if (c == 0)
  1172.                     br->display_lines[br->lines_in_url++] = strdup ("");
  1173.             } else
  1174.                 add_char (p, br, c);
  1175.             continue;
  1176.         }
  1177.  
  1178.         switch (c)    {
  1179.             case '\r':
  1180.                 break;        /* eat it */
  1181.  
  1182.             case '&':
  1183.                 switch (tolower (fgetc (fp)))    {
  1184.                     case 'l':
  1185.                         if (tolower (fgetc (fp)) == 't')    {
  1186.                             add_char (p, br, '<');
  1187.                             p->eat_semicolon = 1;
  1188.                         }
  1189.                         break;
  1190.                     case 'g':
  1191.                         if (tolower (fgetc (fp)) == 't')    {
  1192.                             add_char (p, br, '>');
  1193.                             p->eat_semicolon = 1;
  1194.                         }
  1195.                         break;
  1196.                     case 'n':
  1197.                         if ((tolower (fgetc (fp)) == 'b') &&
  1198.                             (tolower (fgetc (fp)) == 's') &&
  1199.                             (tolower (fgetc (fp)) == 'p'))    {
  1200.                                 add_char (p, br, NBSP);
  1201.                                 p->eat_semicolon = 1;
  1202.                         }
  1203.                         break;
  1204.                     default:
  1205.                         break;
  1206.                 }
  1207.                 break;
  1208.             case '<':
  1209.                 tag = parse_tag (fp);
  1210.                 switch (opcode_lookup (supported_opcodes, tag->name))    {
  1211.                     case OP_CONSUME:    /* used for SCRIPT tag */
  1212.                         br->consume = (!tag->endtag) ? 1 : 0;
  1213.                         break;
  1214.                     case OP_IGNORE:        /* used when a HTML comment is skipped */
  1215.                         break;
  1216.                     case OP_FORM:
  1217.                         if (!tag->endtag)    {
  1218.                             cp = find_option (tag, "action", 0);
  1219.                             if (cp == NULLCHAR)
  1220.                                 break;
  1221.                             br->form = (struct form *) callocw (1, sizeof(struct form));
  1222.                             
  1223.                             cp = rebuild_url (br, cp);
  1224.                             if (cp == NULLCHAR)
  1225.                                 break;
  1226.  
  1227.                             br->form->action = strdup (cp);
  1228.                             cp = find_option (tag, "enctype", 0);
  1229.                             br->form->enctype = (cp == NULLCHAR) ? strdup ("application/x-www-form-urlencoded") : cp;
  1230.                             
  1231.                             cp = find_option (tag, "method", 0);
  1232.                             if (cp != NULLCHAR && !strcasecmp (cp, "GET"))
  1233.                                 br->form->method = 1;
  1234.                             free (cp);
  1235.                         } else    {
  1236.                             struct formlink *fl2;
  1237.                             int found;
  1238.                             /* we need to insure that one radio button in each group is selected */
  1239.                             for (fl = br->form->flink; fl != NULLFLINK; fl = fl->next)    {
  1240.                                 if (fl->type == INPUT_RADIO && !fl->bool)    {
  1241.                                     found = 0;
  1242.                                     for (fl2 = br->form->flink; fl2 != NULLFLINK; fl2 = fl2->next)    {
  1243.                                         if (fl2 != fl && fl2->type == INPUT_RADIO && !strcmp (fl->name, fl2->name))    {
  1244.                                             if (fl2->bool)    {
  1245.                                                 found = 1;
  1246.                                                 break;
  1247.                                             }
  1248.                                         }
  1249.                                     }
  1250.                                     if (!found)    {
  1251.                                         fl->bool = 1;
  1252.                                         br->display_lines[fl->line][fl->col] = '*';
  1253.                                     }
  1254.                                         
  1255.                                 }
  1256.                             }
  1257.                         }
  1258.                         break;
  1259.                         
  1260.                     case OP_PRE:
  1261.                         p->preformatted = (tag->endtag) ? 0 : 1;
  1262.                         break;
  1263.                     case OP_OL:
  1264.                         if (!tag->endtag)
  1265.                             p->listtype[p->listlevel++] = 1;
  1266.                         else if (p->listlevel)
  1267.                             p->listlevel--;
  1268.                         goto do_a_p;
  1269.                     case OP_UL:
  1270.                         if (!tag->endtag)
  1271.                             p->listtype[p->listlevel++] = 0;
  1272.                         else if (p->listlevel)
  1273.                             p->listlevel--;
  1274.                         goto do_a_p;
  1275.                     case OP_DL:
  1276.                         p->listlevel = 0;
  1277.                         goto do_a_p;
  1278.                     case OP_DT:
  1279.                         if (!tag->endtag)
  1280.                             break_line (br, p);
  1281.                         p->listlevel = 0;
  1282.                         break;
  1283.                     case OP_DD:
  1284.                         if (!tag->endtag)    {
  1285.                             p->listlevel = 1;
  1286.                             break_line (br, p);
  1287.                         }
  1288.                         break;
  1289.                     case OP_LI:
  1290.                         if (!tag->endtag)    {
  1291.                             break_line (br, p);
  1292.                             p->listcount[p->listlevel - 1]++;
  1293.                             if (p->listtype[p->listlevel - 1])
  1294.                                 sprintf (p->buf, "%*.*s%2d. ", (p->listlevel * 4),
  1295.                                     (p->listlevel * 4), " ", p->listcount[p->listlevel - 1]);
  1296.                             else
  1297.                                 sprintf (p->buf, "%*.*s*   ", (p->listlevel * 4),
  1298.                                     (p->listlevel * 4), " ");
  1299.                             p->index = (int) strlen (p->buf);
  1300.                             p->lastwhite = p->index - 1;
  1301.                         }
  1302.                         break;
  1303.                         
  1304.                     case OP_IMAGE:
  1305.                         /* look within here to see if an 'alt="xxx"' part is found */
  1306.                         cp = find_option (tag, "alt", 0);
  1307.                         if (cp == NULLCHAR)
  1308.                             break;
  1309.                         add_string (p, br, cp);
  1310.                         free (cp);
  1311.                         break;
  1312.  
  1313.                     case OP_ISINDEX:
  1314.                         br->has_an_index = 1;
  1315.                         break;
  1316.                     case OP_H1:
  1317.                         if (!tag->endtag)    {
  1318.                             break_line (br, p);
  1319.                             break;
  1320.                         }
  1321.                         /* first adjust the buffer to center the line */
  1322.                         p->center_one = 1;
  1323.                         /* and fall through */
  1324.                     case OP_HEADER:
  1325.                     case OP_P:
  1326. do_a_p:                        break_line (br, p);
  1327.                         if (br->lines_in_url && *br->display_lines[br->lines_in_url - 1])
  1328.                             br->display_lines[br->lines_in_url++] = strdup ("");
  1329.                         break;
  1330.                     case OP_TITLE:
  1331.                         if (!tag->endtag)    {
  1332.                             p->title_input = 1;
  1333.                             p->titleindex = 0;
  1334.                         } else    {
  1335.                             p->title_input = 0;
  1336.                             p->title[p->titleindex] = 0;
  1337.                             free (br->title);
  1338.                             br->title = strdup (p->title);
  1339.                         }
  1340.                         break;
  1341.                     case OP_MARQUEE:
  1342.                         if (!tag->endtag)    {
  1343.                             p->marquee_input = 1;
  1344.                             p->marqueeindex = 0;
  1345.                         } else    {
  1346.                             p->marquee_input = 0;
  1347.                             for (c = 0; c < SCREENwidth; c++)
  1348.                                 p->marquee[p->marqueeindex++] = ' ';
  1349.                             p->marquee[p->marqueeindex] = 0;
  1350.                             free (br->marquee);
  1351.                             br->marquee = strdup (p->marquee);
  1352.                         }
  1353.                         break;
  1354.                     case OP_TABLE:
  1355.                         if (!tag->endtag)    {
  1356.                             cp = find_option (tag, "border", 0);
  1357.                             if (cp == NULLCHAR)
  1358.                                 c = 1;
  1359.                             else
  1360.                                 c = atoi (cp);
  1361.                             free (cp);
  1362.                             parse_and_format_table (br, p, fp, c);
  1363.                         }
  1364.                         break;
  1365.                     case OP_CENTER:
  1366.                         if (!tag->endtag)    {
  1367.                             p->center = p->center_one = 0;
  1368.                             break_line (br, p);
  1369.                             p->center = 1;
  1370.                         } else    {
  1371.                             p->center = 0;
  1372.                             p->center_one = 1;
  1373.                             break_line (br, p);
  1374.                         }
  1375.                         break;
  1376.                     case OP_BR:
  1377.                         break_line (br, p);
  1378.                         break;
  1379.                     case OP_HR:
  1380.                         break_line (br, p);
  1381.                         br->display_lines[br->lines_in_url++] = strdup (HLINE);
  1382.                         break;
  1383.                     case OP_FRAME:
  1384.                         cp = find_option (tag, "src", 0);
  1385.                         if (cp == NULLCHAR)
  1386.                             break;
  1387.                         cp = rebuild_url (br, cp);
  1388.                         if (cp == NULLCHAR)
  1389.                             break;
  1390.                         break_line (br, p);
  1391.                             add_char (p, br, NBSP);
  1392.                             add_char (p, br, NBSP);
  1393.                             add_char (p, br, NBSP);
  1394.                         start_link (p, br, cp, NULLFLINK, '[');
  1395.                         add_string (p, br, "Frame: ");
  1396.                         cp = find_option (tag, "name", 0);
  1397.                         if (cp == NULLCHAR)
  1398.                             cp = find_option (tag, "src", 0);
  1399.                         add_string (p, br, cp);
  1400.                         free (cp);
  1401.                         end_link (p, br, ']');
  1402.                         break_line (br, p);
  1403.                         break;
  1404.                     case OP_A:
  1405.                         if (!tag->endtag)    {
  1406.                             cp = find_option (tag, "href", 0);
  1407.                             if (cp == NULLCHAR)
  1408.                                 break;
  1409.  
  1410.                             cp = rebuild_url (br, cp);
  1411.                             if (cp == NULLCHAR)
  1412.                                 break;
  1413.                             start_link (p, br, cp, NULLFLINK, '[');
  1414.                             linkfound = 1;
  1415.                         } else    if (linkfound)    {
  1416.                             if ((br->links[br->link_count].startcolumn + 1) == p->index)
  1417.                                 /* empty, so we'll assume there is a graphic here */
  1418.                                 add_string (p, br, "Graphic");
  1419.  
  1420.                             end_link (p, br, ']');
  1421.                             linkfound = 0;
  1422.  
  1423.                         }
  1424.                         break;
  1425.                     case OP_INPUT:
  1426.                         if (br->form == NULLFORM)
  1427.                             break;
  1428.                         cp = find_option (tag, "type", 0);
  1429.                         if (cp == NULLCHAR)    /* spec says it's required, but the major browsers let it slide and default */
  1430.                             k = INPUT_TEXT;
  1431.                         else    {
  1432.                             k = opcode_lookup (supported_inputs, cp);
  1433.                             free (cp);
  1434.                             if (k == INPUT_UNKNOWN)
  1435.                                 break;
  1436.                         }
  1437.                         fl = (struct formlink *) callocw (1, sizeof(struct formlink));
  1438.                         fl->type = k;
  1439.                         if (br->form->lastlink == NULLFLINK)
  1440.                             br->form->lastlink = br->form->flink = fl;
  1441.                         else    {
  1442.                             br->form->lastlink->next = fl;
  1443.                             br->form->lastlink = fl;
  1444.                         }
  1445.                         fl->name = find_option (tag, "name", 0);
  1446.                         fl->value = find_option (tag, "value", 0);
  1447.                         fl->align = find_option (tag, "align", 0);
  1448.  
  1449.                         cp = find_option (tag, "size", 0);
  1450.                         if (cp != NULLCHAR)    {
  1451.                             fl->size = atoi (cp);
  1452.                             free (cp);
  1453.                         }
  1454.                         cp = find_option (tag, "maxlength", 0);
  1455.                         if (cp != NULLCHAR)    {
  1456.                             fl->maxlength = atoi (cp);
  1457.                             free (cp);
  1458.                         }
  1459.  
  1460.                         cp = find_option (tag, "selected", &k);
  1461.                         if (k)
  1462.                             fl->bool = 1;
  1463.                         free (cp);
  1464.  
  1465.                         if (fl->type == INPUT_HIDDEN)
  1466.                             break;
  1467.                         start_link (p, br, NULLCHAR, fl, (fl->type != INPUT_RADIO) ? '[' : '(');
  1468.                         fl->line = br->lines_in_url;
  1469.                         fl->col = p->index;
  1470.                         switch (fl->type)    {
  1471.                             case INPUT_CHECKBOX:
  1472.                                 add_char (p, br, (fl->bool) ? 'X' : '_');
  1473.                                 break;
  1474.                             case INPUT_RADIO:
  1475.                                 add_char (p, br, (fl->bool) ? '*' : '_');
  1476.                                 break;
  1477.                             case INPUT_IMAGE:
  1478.                                 add_string (p, br, "Clickable Image");
  1479.                                 break;
  1480.                             case INPUT_SUBMIT:
  1481.                                 add_string (p, br, (fl->value) ? fl->value : "Submit");
  1482.                                 break;
  1483.                             case INPUT_RESET:
  1484.                                 add_string (p, br, (fl->value) ? fl->value : "Reset");
  1485.                                 break;
  1486.                             case INPUT_PASSWORD:
  1487.                             case INPUT_TEXT:
  1488.                                 for (k = 0; k < fl->size; k++)
  1489.                                     add_char (p, br, '_');
  1490.                                 break;
  1491.                             default:
  1492.                                 break;
  1493.                         }
  1494.                         end_link (p, br, (fl->type != INPUT_RADIO) ? ']' : ')');
  1495.                         add_char (p, br, ' ');
  1496.                         
  1497.                         break;
  1498.                     default:
  1499.                         break;
  1500.                 }
  1501.                 delete_tag (tag);
  1502.                 break;
  1503.             default:
  1504.                 add_char (p, br, c);
  1505.                 break;
  1506.         }
  1507.     }
  1508.     break_line (br, p);
  1509.  
  1510.     /* now check for longest line length, and set it */
  1511.     br->longestline = br->columnoffset = 0;
  1512.     for (k = 0; k < br->lines_in_url; k++)    {
  1513.         if ((int) strlen (br->display_lines[k]) > br->longestline)
  1514.             br->longestline = (int) strlen (br->display_lines[k]);
  1515.     }
  1516.  
  1517. #ifndef TEST
  1518.     if (savefp != NULLFILE)
  1519.         (void) fclose (savefp);
  1520. #endif
  1521.  
  1522.     /* if both an ISINDEX and an MARQUEE found, ISINDEX wins the status line */
  1523.     if (br->has_an_index && br->marquee)    {
  1524.         free (br->marquee);
  1525.         br->marquee = NULLCHAR;
  1526.     }
  1527. }
  1528.  
  1529.  
  1530.  
  1531. static char *
  1532. get_tag_data (struct browser *br, FILE *fp, char **cur_url, struct tag **cur_tag)
  1533. {
  1534. int theindex, c;
  1535. char buf[1024], *cp;
  1536. struct tag *tag;
  1537. int done = 0;
  1538. int eat_semicolon = 0;
  1539.  
  1540.     theindex = 0;
  1541.     buf[0] = 0;
  1542.  
  1543.     do    {
  1544.         while ((c = fgetc (fp)) != '<')    {
  1545. #if 0
  1546.             if (c != '\n')
  1547.                 buf[theindex++] = (char) c;
  1548. #else
  1549.             switch (c)    {
  1550.                 case '&':
  1551.                     switch (tolower (fgetc (fp)))    {
  1552.                         case 'l':
  1553.                             if (tolower (fgetc (fp)) == 't')    {
  1554.                                 buf[theindex++] = '<';
  1555.                                 eat_semicolon = 1;
  1556.                             }
  1557.                             break;
  1558.                         case 'g':
  1559.                             if (tolower (fgetc (fp)) == 't')    {
  1560.                                 buf[theindex++] = '>';
  1561.                                 eat_semicolon = 1;
  1562.                             }
  1563.                             break;
  1564.                         case 'n':
  1565.                             if ((tolower (fgetc (fp)) == 'b') &&
  1566.                                 (tolower (fgetc (fp)) == 's') &&
  1567.                                 (tolower (fgetc (fp)) == 'p'))    {
  1568.                                     buf[theindex++] = ' ';
  1569.                                     eat_semicolon = 1;
  1570.                             }
  1571.                             break;
  1572.                         default:
  1573.                             break;
  1574.                     }
  1575.                     break;
  1576.                 case '\n':
  1577.                     break;    /* nothing, eat it */
  1578.                 case ';':
  1579.                     if (eat_semicolon)    {
  1580.                         eat_semicolon = 0;
  1581.                         break;
  1582.                     }
  1583.                     /* else, fall-through */
  1584.                 default:
  1585.                     buf[theindex++] = (char) c;
  1586.                     eat_semicolon = 0;
  1587.             }
  1588. #endif
  1589.         }
  1590.         tag = parse_tag (fp);
  1591.         switch (opcode_lookup (supported_opcodes, tag->name))    {
  1592.             case OP_A:    /* do 'A' tags here */
  1593.                 if (!tag->endtag)    {
  1594.                     cp = find_option (tag, "href", 0);
  1595.                     if (cp != NULLCHAR)    {
  1596.                         cp = rebuild_url (br, cp);
  1597.                         if (cp == NULLCHAR)
  1598.                             break;
  1599.                         /* this only occurs if this data element had multiple links -
  1600.                            all but the last one are lost */
  1601.                         if (*cur_url != NULLCHAR)
  1602.                             free (*cur_url);
  1603.                         *cur_url = cp;
  1604.                     }
  1605.                 }
  1606.                 delete_tag (tag);
  1607.                 break;
  1608.             case OP_CAPTION:
  1609.             case OP_TR:
  1610.             case OP_TH:
  1611.             case OP_TD:
  1612.             case OP_TABLE:
  1613.                 delete_tag (*cur_tag);
  1614.                 *cur_tag = tag;
  1615.                 done = 1;
  1616.                 break;
  1617.             default:    /* ignore it */
  1618.                 delete_tag (tag);
  1619.                 break;
  1620.         }
  1621.     } while (!done);
  1622.     buf[theindex] = 0;
  1623.     return (strdup (buf));
  1624. }
  1625.  
  1626.  
  1627.  
  1628. static void
  1629. parse_and_format_table (struct browser *br, struct parseparams *p, FILE *fp, int border)
  1630. {
  1631. int c, done = 0, theindex, in_a_row = 0, offset;
  1632. int column = 0, usecols, userows, size, thissize;
  1633. int rowsize, tmp;
  1634. struct tag *tag;
  1635. char *cp;
  1636. struct form_table *tbl;
  1637. struct table_row *row, *newrow;
  1638. struct table_column *col;
  1639. int nowrap;
  1640. char *cur_url = NULLCHAR;
  1641. int reloop;
  1642. int tablelevel = 1;
  1643. int foundimbedded = 0;
  1644.  
  1645.     tbl = (struct form_table *) callocw (1, sizeof (struct form_table));
  1646.     row = tbl->rows;
  1647.  
  1648.     /* first we must parse the entire table tag (and sub-tags) */
  1649.     while (!done && (c = fgetc (fp)) != EOF)    {
  1650.         if (c != '<')
  1651.             continue;
  1652.         tag = parse_tag (fp);
  1653. loop:        reloop = 0;
  1654.         switch (opcode_lookup (supported_opcodes, tag->name))    {
  1655.             case OP_CAPTION:
  1656.                 if (!tag->endtag)    {
  1657.                     if (tbl->caption)
  1658.                         free (tbl->caption);
  1659.                     tbl->caption = get_tag_data (br, fp, &cur_url, &tag);
  1660.                     reloop = 1;
  1661.                 }
  1662.                 break;
  1663.             case OP_TR:    /* define a table row */
  1664.                 if (tag->endtag || in_a_row)    {
  1665.                     /* ending a row */
  1666.                     in_a_row = 0;
  1667.                     if (tbl->rows[tbl->numrows].cols_in_row > tbl->numcols)
  1668.                         tbl->numcols = tbl->rows[tbl->numrows].cols_in_row;
  1669.                     tbl->numrows++;
  1670.                 }
  1671.                 if (!tag->endtag)    {
  1672.                     row = &tbl->rows[tbl->numrows];
  1673.                     in_a_row = 1;
  1674.                     column = row->cols_in_row;
  1675.                     cp = find_option (tag, "align", 0);
  1676.                     if (cp)    {
  1677.                         if (!strcasecmp ("center", cp))
  1678.                             row->align = TABLE_ALIGN_CENTER;
  1679.                         else if (!strcasecmp ("right", cp))
  1680.                             row->align = TABLE_ALIGN_RIGHT;
  1681.                         free (cp);
  1682.                     }
  1683.                 }
  1684.  
  1685.                 if (column > tbl->numcols)
  1686.                     tbl->numcols = column;
  1687.                 break;
  1688.             case OP_TH:    /* define a table heading */
  1689.             case OP_TD:    /* define a table data element */
  1690.                 if (tag->endtag)
  1691.                     break;
  1692.                 usecols = 1;
  1693.                 col = &row->cols[row->cols_in_row];
  1694.                 cp = find_option (tag, "colspan", 0);
  1695.                 if (cp != NULLCHAR)
  1696.                     usecols = atoi (cp);
  1697.                 if (!usecols)
  1698.                     usecols = 1;
  1699.                 col->colspan = usecols;
  1700.                 free (cp);
  1701.  
  1702.                 userows = 1;
  1703.                 cp = find_option (tag, "rowspan", 0);
  1704.                 if (cp != NULLCHAR)
  1705.                     userows = atoi (cp);
  1706.                 if (!userows)
  1707.                     userows = 1;
  1708.                 free (cp);
  1709.  
  1710.                 col->align = row->align;
  1711.                 cp = find_option (tag, "align", 0);
  1712.                 if (cp)    {
  1713.                     if (!strcasecmp ("center", cp))
  1714.                         col->align = TABLE_ALIGN_CENTER;
  1715.                     else if (!strcasecmp ("right", cp))
  1716.                         col->align = TABLE_ALIGN_RIGHT;
  1717.                     free (cp);
  1718.                 }
  1719.  
  1720.                 col->rowspan = userows;
  1721.                 if (userows != 1)    {
  1722.                     newrow = &tbl->rows[tbl->numrows + 1];
  1723.                     newrow->cols[newrow->cols_in_row].colspan = usecols;
  1724.                     newrow->cols[newrow->cols_in_row].rowspan = 1;
  1725.                     newrow->cols_in_row++;
  1726.                     if (usecols != 1)    {
  1727.                         newrow->cols[newrow->cols_in_row].colspan = 1;
  1728.                         newrow->cols[newrow->cols_in_row].rowspan = 1;
  1729.                         newrow->cols_in_row++;
  1730.                     }
  1731.                 }
  1732.  
  1733.                 cur_url = NULLCHAR;
  1734.                 col->value = get_tag_data (br, fp, &cur_url, &tag);
  1735.                 reloop = 1;
  1736.  
  1737.                 /* now, if a A tag was found in this column, save it */
  1738.                 if (cur_url != NULLCHAR)
  1739.                     col->url = cur_url;
  1740.  
  1741.                 rowsize = (int) strlen(col->value) / usecols;
  1742.                 if (tbl->colsize[column] < rowsize)
  1743.                     tbl->colsize[column] = rowsize;
  1744.                     
  1745.                 row->cols_in_row++;
  1746.                  column++;
  1747.                 if (usecols != 1)    {
  1748.                     while (--usecols)    {
  1749.                         col++;
  1750.                         col->colspan = 1;
  1751.                         col->rowspan = userows;
  1752.                         row->cols_in_row++;
  1753.                         tbl->colsize[column] = rowsize;
  1754.                          column++;
  1755.                     }
  1756.                 }
  1757.                 break;
  1758.             case OP_TABLE:
  1759.                 /* this SHOULD be an endtag - assumed */
  1760.                 if (!tag->endtag)    {
  1761.                     tablelevel++;
  1762.                     foundimbedded++;
  1763.                 } else
  1764.                     tablelevel--;
  1765.                 if (tablelevel == 0)
  1766.                     done = 1;
  1767.                 break;
  1768.             default:
  1769.                 break;
  1770.         }
  1771.         if (reloop)
  1772.             goto loop;
  1773.         delete_tag (tag);
  1774.     }
  1775.     if (in_a_row)
  1776.         tbl->numrows++;
  1777.  
  1778.  
  1779.     if (foundimbedded)    {
  1780.         break_line (br, p);
  1781.         add_string (p, br, "[ The following is a table with one or more imbedded tables - TNOS doesn't render these properly (at least not yet) ]");
  1782.         if (br->lines_in_url && *br->display_lines[br->lines_in_url - 1])
  1783.             br->display_lines[br->lines_in_url++] = strdup ("");
  1784.         break_line (br, p);
  1785.     }
  1786.  
  1787.     /* now we set column sizes where there were no headers for the columns */
  1788.     for (c = 0; c < tbl->numcols; c++)    {
  1789.         if (!tbl->colsize[c])    {    /* wasn't set by a header */
  1790.             column = 0;
  1791.             size = 1;
  1792.             for (theindex = 0, row = tbl->rows; theindex < tbl->numrows; theindex++,row++)    {
  1793.                 for (usecols = 0,col = row->cols; usecols < row->cols_in_row; usecols++,col++)    {
  1794.                     if (column < c)    {
  1795.                         column++;
  1796.                         thissize = (int) strlen (col->value) / col->rowspan;
  1797.                         if (col->value != NULLCHAR && size < thissize)
  1798.                             size = thissize;
  1799.                     }
  1800.                 }
  1801.             }
  1802.             tbl->colsize[c] = size;
  1803.         }
  1804.     }
  1805.     
  1806. #ifdef TEST
  1807.     printf ("Table:\n\tCaption: %s - Numcols: %d - Numrows: %d\n",
  1808.         (tbl->caption) ? tbl->caption : "none", tbl->numcols, tbl->numrows);
  1809.     for (c = 0; c < tbl->numcols; c++)
  1810.         printf ("\tColumn %d: size: %d", c, tbl->colsize[c]);
  1811.     for (c = 0; c < tbl->numrows; c++)    {
  1812.         int cc;
  1813.         printf ("\tRow %d - cols_in_row: %d - align: %d\n",
  1814.             c, tbl->rows[c].cols_in_row, tbl->rows[c].align);
  1815.         for (cc = 0; cc < tbl->rows[c].cols_in_row; cc++)
  1816.             printf ("\tCol %d - colspan: %d, rowspan: %d, align: %d, value: '%s'\n",
  1817.                 cc, tbl->rows[c].cols[cc].colspan,
  1818.                 tbl->rows[c].cols[cc].rowspan, tbl->rows[c].cols[cc].align,
  1819.                 tbl->rows[c].cols[cc].value);
  1820.     }
  1821. #endif
  1822.     /* now we render the table */
  1823.     size = 1 + (tbl->numcols * 3);
  1824.     for (c = 0; c < tbl->numcols; c++)
  1825.         size += tbl->colsize[c];
  1826.  
  1827.     break_line (br, p);
  1828.     nowrap = p->nowrap;
  1829.     p->nowrap = 1;
  1830.     
  1831.     /* render top line */
  1832.     if (border)    {
  1833.         for (c = 0; c < size; c++)
  1834.             add_char (p, br, '-');
  1835.         break_line (br, p);
  1836.     }
  1837.  
  1838.     /* render data lines */
  1839.     for (c = 0; c < tbl->numrows; c++)    {
  1840.         int sizes[MAX_TABLE_COLS];
  1841.         int thetypes[MAX_TABLE_COLS];
  1842.         int centering;
  1843.         int strsize;
  1844.         int numsizes = 0;
  1845.         int width;
  1846.         add_char (p, br, (border) ? '|' : ' ');
  1847.         row = &tbl->rows[c];
  1848.         column = 0;
  1849.         for (theindex = 0; theindex < row->cols_in_row; theindex += offset)    {
  1850.             width = 0;
  1851.             for (userows = 0; userows < row->cols[theindex].colspan; userows++)    {
  1852.                 if (width)
  1853.                     width += 3;
  1854.                 width += tbl->colsize[theindex + userows];
  1855.             }
  1856.             sizes[numsizes] = width;
  1857.             if (row->cols[theindex].value && width < (int) strlen (row->cols[theindex].value) && row->cols[theindex].rowspan != 1)    {
  1858.                 /* we can split the data, and put some on the next line */
  1859.                 /* this next line makes assumptions which will break */
  1860.                 tbl->rows[c + 1].cols[theindex].value = strdup (&row->cols[theindex].value[width - 1]);
  1861.                 row->cols[theindex].value[width - 1] = 0;
  1862.             }
  1863.             column++;
  1864.             offset = row->cols[theindex].colspan;
  1865.             if (row->cols[theindex].url != NULLCHAR)
  1866.                 start_link (p, br, row->cols[theindex].url, NULLFLINK, 0);
  1867.             add_char (p, br, NBSP);
  1868.  
  1869.             /* alignment changes, if not left */
  1870.             strsize = (row->cols[theindex].value) ? (int) strlen (row->cols[theindex].value) : 0;
  1871.             centering = width - strsize;
  1872.             if (centering < 0)
  1873.                 centering = 0;
  1874.             switch (row->cols[theindex].align)    {
  1875.                 case TABLE_ALIGN_RIGHT:
  1876.                     while (centering--)
  1877.                         add_char (p, br, NBSP);
  1878.                     if (row->cols[theindex].value)
  1879.                         add_string (p, br, row->cols[theindex].value);
  1880.                     break;
  1881.                 case TABLE_ALIGN_CENTER:
  1882.                     tmp = centering / 2;
  1883.                     centering -= tmp;
  1884.                     while (tmp--)
  1885.                         add_char (p, br, NBSP);
  1886.                     if (row->cols[theindex].value)
  1887.                         add_string (p, br, row->cols[theindex].value);
  1888.                     while (centering--)
  1889.                         add_char (p, br, NBSP);
  1890.                     break;
  1891.                 case TABLE_ALIGN_LEFT:
  1892.                 default:
  1893.                     if (row->cols[theindex].value)
  1894.                         add_string (p, br, row->cols[theindex].value);
  1895.                     while (centering--)
  1896.                         add_char (p, br, NBSP);
  1897.                     break;
  1898.             }
  1899.             thetypes[numsizes] = (row->cols[theindex].rowspan == 1) ? 1 : 0;
  1900.             add_char (p, br, NBSP);
  1901.             if (row->cols[theindex].url != NULLCHAR)
  1902.                 end_link (p, br, 0);
  1903.             add_char (p, br, (border) ? '|' : ' ');
  1904.             numsizes++;
  1905.         }
  1906.         break_line (br, p);
  1907.  
  1908.         /* render dividing line line */
  1909.         if (border && c < (tbl->numrows - 1))    {
  1910.             add_char (p, br, '|');
  1911.             for (theindex = 0; theindex < numsizes; theindex++)    {
  1912.                 int i, ch;
  1913.                 ch = (thetypes[theindex]) ? '-' : NBSP;    /*lint !e771 */
  1914.                 add_char (p, br, ch);
  1915.                 for (i = 0; i < sizes[theindex]; i++)    /*lint !e771 */
  1916.                     add_char (p, br, ch);
  1917.                 add_char (p, br, ch);
  1918.                 add_char (p, br, '|');
  1919.             }
  1920.             break_line (br, p);
  1921.         }
  1922.     }
  1923.     
  1924.     /* render bottom line */
  1925.     if (border)    {
  1926.         for (c = 0; c < size; c++)
  1927.             add_char (p, br, '-');
  1928.         break_line (br, p);
  1929.     }
  1930.  
  1931.     /* and we add the caption line, if defined */
  1932.     if (tbl->caption)    {
  1933.         if (!border && br->lines_in_url && *br->display_lines[br->lines_in_url - 1])
  1934.             br->display_lines[br->lines_in_url++] = strdup ("");
  1935.  
  1936.         for (c = (size - (int)(strlen (tbl->caption))) / 2; c > 0; c--)
  1937.             add_char (p, br, NBSP);
  1938.         add_string (p, br, tbl->caption);
  1939.         break_line (br, p);
  1940.     }
  1941.     p->nowrap = nowrap;
  1942.  
  1943.     if (foundimbedded)    {
  1944.         break_line (br, p);
  1945.         if (br->lines_in_url && *br->display_lines[br->lines_in_url - 1])
  1946.             br->display_lines[br->lines_in_url++] = strdup ("");
  1947.         add_string (p, br, "[ End of table with one or more imbedded tables ]");
  1948.         break_line (br, p);
  1949.     }
  1950.  
  1951.     /* finally, we delete the table structure */
  1952.     free (tbl->caption);
  1953.     for (c = 0, row = tbl->rows; c < tbl->numrows; c++, row++)    {
  1954.         int l;
  1955.         for (l = 0, col = row->cols; l < row->cols_in_row; l++, col++)
  1956.             free (col->value);
  1957.     }
  1958. }
  1959.  
  1960.  
  1961. static void
  1962. trim_trailing_white (char *str)
  1963. {
  1964. int len;
  1965.  
  1966.     for (len = (int) strlen (str); len > 0; len--)
  1967.         if (str[len] == ' ')
  1968.             str[len] = 0;
  1969. }
  1970.  
  1971.  
  1972.  
  1973. #ifndef TEST
  1974.  
  1975. static void
  1976. add_cookie (char *host, char *path, time_t expires, char *name, char *value)
  1977. {
  1978. FILE *fp;
  1979.  
  1980.     fp = fopen (Cookiejar, APPEND_TEXT);
  1981.     if (fp != NULLFILE)    {
  1982.         trim_trailing_white (host);
  1983.         trim_trailing_white (path);
  1984.         trim_trailing_white (name);
  1985.         trim_trailing_white (value);
  1986.         fprintf (fp, "%s %s %ld %s %s\n", host, path, expires, name, value);
  1987.         (void) fclose (fp);
  1988.     }
  1989. }
  1990.  
  1991.  
  1992.  
  1993. static void
  1994. share_cookies (int s, char const *host, char const *component)
  1995. {
  1996. FILE *fp;
  1997. char buf[512], *cp, *tmp;
  1998. time_t thetime;
  1999. time_t expires;
  2000.  
  2001.     (void) time (&thetime);
  2002.     fp = fopen (Cookiejar, READ_TEXT);
  2003.     if (fp != NULLFILE)    {
  2004.         while (fgets (buf, 512, fp) != NULLCHAR)    {
  2005.             if ((cp = strchr (buf, ' ')) == NULLCHAR)
  2006.                 continue;
  2007.             *cp++ = 0;
  2008.  
  2009.             /* if not for this host, skip it */
  2010.             if (strcasecmp (host, buf))
  2011.                 continue;
  2012.             
  2013.             if ((tmp = strchr (cp, ' ')) == NULLCHAR)
  2014.                 continue;
  2015.             *tmp++ = 0;
  2016.  
  2017.             /* if not for this path prefix, skip it */
  2018.             if (strncmp (cp, component, strlen (cp)))
  2019.                 continue;
  2020.  
  2021.             /* if already expired, skip it */
  2022.             expires = (time_t) atol (tmp);
  2023.             if (thetime > expires)
  2024.                 continue;
  2025.  
  2026.             if ((cp = strchr (tmp, ' ')) == NULLCHAR)
  2027.                 continue;
  2028.             if ((tmp = strchr (++cp, ' ')) == NULLCHAR)
  2029.                 continue;
  2030.             *tmp++ = 0;
  2031.             rip (tmp);
  2032.  
  2033.             /* cp now points to the cookie name, and tmp to it's value */
  2034.             /* need to add code to consolidate multiple cookies to 1 line */
  2035.             usprintf (s, "Cookie: %s=%s\n", cp, tmp);
  2036.         }
  2037.         (void) fclose (fp);
  2038.     }
  2039. }
  2040.  
  2041.  
  2042.  
  2043. static void
  2044. blank_status (struct browser *br)
  2045. {
  2046. int k, i;
  2047.  
  2048.     goto_stat (br, 0);
  2049.     for (k = 0; k < STATUS_LINES; k++)
  2050.         for (i = 0; i < SCREENwidth; i++)
  2051.             if (i != (SCREENwidth - 1) || k != (STATUS_LINES - 1))
  2052.                 cputs (" ");
  2053. }
  2054.  
  2055.  
  2056.  
  2057. static void
  2058. goto_stat (struct browser *br, int plusoffset)
  2059. {
  2060.     gotoxy (1, br->current_screen_lines + plusoffset + Current->screen->statline + 1);
  2061. }
  2062.  
  2063.  
  2064.  
  2065. static void
  2066. render_screen (struct browser *br)
  2067. {
  2068. int countdown, k, l;
  2069. struct url_links *curlink;
  2070. int startcol, endcol;
  2071. char linebuf[256];
  2072. char partial[256];
  2073.  
  2074.     clrscr ();
  2075.     curlink = &br->links[br->current_link];
  2076.     br->current_screen_lines = countdown = SCREENlength - ((Current->screen->statline * 2) + 3);
  2077.     
  2078.     if (Current->screen->statline)
  2079.         cputs (Eol);
  2080.  
  2081.     /* first put the title up at the top right */
  2082.     if (br->title)    {
  2083.         countdown -= 2;
  2084.         endcol = (int) strlen (br->title);
  2085.         startcol = (SCREENwidth - 1) - endcol;
  2086.         if (startcol < 0)    {
  2087.             endcol = (SCREENwidth - 1);
  2088.             startcol = 0;
  2089.         }
  2090.         while (startcol--)
  2091.             cputs (" ");
  2092.         strncpy (partial, br->title, (SCREENwidth - 1));
  2093.         cputs (partial);
  2094.         cputs (Eol);
  2095.         cputs (Eol);
  2096.     }
  2097.             
  2098.     for (k = br->current_top_line; countdown && k < br->lines_in_url; k++, countdown--)    {
  2099.         if ((int) strlen (br->display_lines[k]) < br->columnoffset)    {
  2100.             linebuf[0] = 0;
  2101.             if (br->columnoffset)
  2102.                 strcpy (linebuf, "<");
  2103.         } else    {
  2104.             strncpy (linebuf, &br->display_lines[k][br->columnoffset], (SCREENwidth + 1));
  2105.             if ((int) strlen (linebuf) > SCREENwidth)    {
  2106.                 linebuf[(SCREENwidth - 1)] = '>';
  2107.                 linebuf[SCREENwidth] = 0;
  2108.             }
  2109.             if (br->columnoffset)
  2110.                 linebuf[0] = '<';
  2111.         }
  2112.         if (br->links_per_line[k] == 0 || curlink->startline > k || curlink->endline < k)    {
  2113.             /* optimize - no special treatment */
  2114.             cputs (linebuf);
  2115.             if ((int) strlen(linebuf) < SCREENwidth)
  2116.                 cputs (Eol);
  2117.             continue;
  2118.         }
  2119.  
  2120.         /* we only do this if the current link is a part of this line */
  2121.         if (curlink->startline == k)
  2122.             startcol = curlink->startcolumn;
  2123.         else    {    /* otherwise, we are continuing on a new line */
  2124.             for (l = 0; l < SCREENwidth; l++)
  2125.                 if (linebuf[l] != ' ')
  2126.                     break;
  2127.             startcol = l;
  2128.         }
  2129.         if (curlink->endline == k)
  2130.             endcol = curlink->endcolumn;
  2131.         else        /* otherwise, we continue on the next line */
  2132.             endcol = (int) (strlen (linebuf) - 1);
  2133.  
  2134.         if (startcol > (int) (strlen(linebuf) - 1))    {
  2135.             /* starts at end of line - optimize */
  2136.             cputs (linebuf);
  2137.             cputs (Eol);
  2138.             continue;
  2139.         }
  2140.             
  2141.         if (startcol)    {
  2142.             strncpy (partial, linebuf, (unsigned int) startcol);
  2143.             partial[startcol] = 0;
  2144.             cputs (partial);
  2145.         }
  2146.         highvideo ();
  2147.         strncpy (partial, &linebuf[startcol], (unsigned int) ((endcol - startcol) + 1));
  2148.         partial[(endcol - startcol) + 1] = 0;
  2149.         cputs (partial);
  2150.         normvideo ();
  2151.         if (endcol != (int) (strlen (linebuf) - 1))
  2152.             cputs (&linebuf[endcol + 1]);
  2153.  
  2154.         cputs (Eol);
  2155.     }
  2156.     /* display a status line, and place the user on an input line */
  2157.     goto_stat (br, 0);
  2158.     if (br->has_an_index)    {
  2159.         cputs (" ** Contains a searchable index - press 's' to enter search string **");
  2160.         cputs (Eol);
  2161.     } else
  2162.         cputs (Eol);
  2163.     sprintf (partial, "[Press '?' for help - Url: %-52.52s]", br->current_url);
  2164.     cputs (partial);
  2165.     if (curlink->url)    {
  2166.         cputs (" link->");
  2167.         cputs (curlink->url);
  2168.     } else    if (curlink->flink) {
  2169.         cputs (" form component: ");
  2170.         switch (curlink->flink->type)    {
  2171.             case INPUT_PASSWORD:
  2172.                 cputs ("non-echoed ");
  2173.                 /* and fall through */
  2174.             case INPUT_TEXT:
  2175.                 cputs ("text field - press <CR> to edit");
  2176.                 break;
  2177.             case INPUT_RESET:
  2178.                 cputs ("press <CR> to clear the form");
  2179.                 break;
  2180.             case INPUT_SUBMIT:
  2181.             case INPUT_IMAGE:
  2182.                 cputs ("press <CR> to send form");
  2183.                 break;
  2184.             case INPUT_CHECKBOX:
  2185.                 cputs ("press <CR> to toggle the state of this checkbox");
  2186.                 break;
  2187.             case INPUT_RADIO:
  2188.                 if (!curlink->flink->bool)
  2189.                     cputs ("press <CR> to select this radio button");
  2190.                 else
  2191.                     cputs ("selected radio button");
  2192.                 break;
  2193.             default:
  2194.                 break;
  2195.         }
  2196.     }
  2197.     kwait (NULL);
  2198. }
  2199.  
  2200.  
  2201.  
  2202. static void
  2203. spawn_telnet (int port, void *host, void *p OPTIONAL)
  2204. {
  2205. char *args[3];
  2206. char *hst, portstr[10];
  2207.  
  2208.     if (port == 80)
  2209.         port = 23;
  2210.     Curproc->input = Command->input;
  2211.     args[0] = strdup ("telnet");
  2212.     args[1] = hst = strdup (host);
  2213.     sprintf (portstr, "%-d", port);
  2214.     args[2] = portstr;
  2215.  
  2216.     if (Current != Command)    {
  2217.         clrscr ();
  2218.         rflush ();
  2219.         kwait (NULL);
  2220.     }
  2221.     (void) dotelnet (3, args, NULL);
  2222.  
  2223.     free (args[0]);
  2224.     free (hst);
  2225. }
  2226.  
  2227.  
  2228.  
  2229. static void
  2230. spawn_ftp (int unused OPTIONAL, void *host, void *file)
  2231. {
  2232. char *args[4];
  2233. char *hst, *fname, *tempname, *cp;
  2234. FILE *fp;
  2235.  
  2236.     hst = strdup (host);
  2237.     fname = strdup (file);
  2238.  
  2239.     /* create a temp file for using ftp non-interactively */
  2240.     tempname = strdup (tmpnam (NULLCHAR));
  2241.     if ((fp = fopen (tempname, "w")) != NULLFILE)    {
  2242.         if ((cp = strrchr (file, '/')) != NULLCHAR)
  2243.             *cp++ = 0;
  2244.         fprintf (fp, "anonymous\nwwwuser@%s\nbinary\ncd %s\nget %s\nbye\n", Hostname, (char *) file, cp);
  2245.         (void) fclose (fp);
  2246.  
  2247.         Curproc->input = Command->input;
  2248.         args[0] = strdup ("ftp");
  2249.         args[1] = hst;
  2250.         args[2] = tempname;
  2251.         args[3] = strdup ("prompt");
  2252.  
  2253.         if (Current != Command)    {
  2254.             clrscr ();
  2255.             rflush ();
  2256.             kwait (NULL);
  2257.         }
  2258.         (void) doftp (4, args, NULL);
  2259.         unlink (tempname);
  2260.  
  2261.         free (args[0]);
  2262.         free (args[3]);
  2263.     }
  2264.  
  2265.     free (hst);
  2266.     free (fname);
  2267.     free (tempname);
  2268. }
  2269.  
  2270.  
  2271.  
  2272. static void
  2273. send_blank_lines (int count)
  2274. {
  2275.     while (count--)
  2276.         cputs (Eol);
  2277. }
  2278.  
  2279.  
  2280.  
  2281. static void
  2282. send_mailto (char *addr)
  2283. {
  2284. char *args[5];
  2285. char *tempname;
  2286. FILE *fp;
  2287. char buf[128];
  2288. char subject[128];
  2289.  
  2290.     /* create a temp file for the email message */
  2291.     sprintf (buf, "<%s", tmpnam (NULLCHAR));
  2292.     tempname = strdup (buf);
  2293.  
  2294.     if ((fp = fopen (&tempname[1], "w")) != NULLFILE)    {
  2295.         clrscr ();
  2296.         send_blank_lines (3);
  2297.         cputs ("Enter the subject for the email message:");
  2298.         send_blank_lines (2);
  2299.         Current->ttystate.edit = Current->ttystate.echo = 1;
  2300.  
  2301.         kwait (NULL);
  2302.         (void) recvline (Curproc->input, (unsigned char *) subject, 128);
  2303.  
  2304.         send_blank_lines (2);
  2305.         cputs ("Enter the body of the message: ('/ex' to end)");
  2306.         cputs (Eol);
  2307.     
  2308.         for ( ; ; )    {
  2309.             (void) recvline (Curproc->input, (unsigned char *) buf, 128);
  2310.             if (!strncasecmp (buf, "/ex", 3))
  2311.                 break;
  2312.             fputs (buf, fp);
  2313.             kwait (NULL);
  2314.         }
  2315.         (void) fclose (fp);
  2316.  
  2317.         args[0] = strdup ("sendmail");
  2318.         args[1] = addr;
  2319.         sprintf (buf, "wwwuser@%s", Hostname);
  2320.         args[2] = buf;
  2321.         args[3] = subject;
  2322.         args[4] = tempname;
  2323.  
  2324.         (void) dosendmail (5, args, NULL);
  2325.         unlink (tempname);
  2326.  
  2327.         clrscr ();
  2328.         send_blank_lines (3);
  2329.         cputs ("The email message has been sent!");
  2330.         cputs (Eol);
  2331.         cputs ("Press any key to return to the current page...");
  2332.         kwait (NULL);
  2333.         Current->ttystate.edit = Current->ttystate.echo = 0;
  2334.         (void) rrecvchar (Curproc->input);
  2335.         free (args[0]);
  2336.     }
  2337.  
  2338.     free (tempname);
  2339. }
  2340.  
  2341.  
  2342.  
  2343.  
  2344. static char *
  2345. encode_post (char *str)
  2346. {
  2347. char *retval, *cp;
  2348. static char hexchar[] = "0123456789ABCDEF";
  2349.  
  2350.     if (str == NULLCHAR || !*str)
  2351.         return (strdup (""));
  2352.  
  2353.     cp = retval = (char *) callocw (1, strlen (str) * 2);
  2354.     while (*str)    {
  2355.         if (*str == ' ')    {
  2356.             *cp++ = '+';
  2357.             str++;
  2358.         } else if (!strchr ("$-_.!*+'(),;:@&=?/%", *str))
  2359.             *cp++ = *str++;
  2360.         else    {
  2361.             *cp++ = '%';
  2362.             *cp++ = hexchar[*str / 16];
  2363.             *cp++ = hexchar[*str++ % 16];
  2364.         }
  2365.     }
  2366.     return retval;
  2367. }
  2368.  
  2369.  
  2370.  
  2371. static int
  2372. gethead (const char *url, char *location, char *modified, char *content)
  2373. {
  2374. FILE *fp;
  2375. char type[12], host[100], component[512];
  2376. int port, s, c;
  2377. char *tmp, *cp;
  2378. struct sockaddr_in fsocket;
  2379.  
  2380.     if (url == NULLCHAR || (location == NULLCHAR && modified == NULLCHAR && content == NULLCHAR))
  2381.         return 0;
  2382.  
  2383.     if (location != NULLCHAR)
  2384.         *location = 0;
  2385.  
  2386.     if (modified != NULLCHAR)
  2387.         *modified = 0;
  2388.  
  2389.     if (content != NULLCHAR)
  2390.         *content = 0;
  2391.         
  2392.     fp = tmpfile ();
  2393.     if (fp == NULLFILE)
  2394.         return -1;    /* shouldn't happen */
  2395.         
  2396.     tmp = strdup (url);
  2397.     if (disect_url (tmp, type, host, &port, component))    {
  2398.         (void) fclose (fp);
  2399.         return 0;
  2400.     }
  2401.     if (!*host)    {
  2402.         strncpy (host, component, 100);
  2403.         strcpy (component, "/");
  2404.     }
  2405.     free (tmp);
  2406.  
  2407.     if (strcasecmp (type, "http"))    {
  2408.         /* don't do anything, for now */
  2409.         (void) fclose (fp);
  2410.         return 0;
  2411.     }
  2412.  
  2413.     s = http_connect (host, port, &fsocket);
  2414.     if (s == -1)    {
  2415.         (void) fclose (fp);
  2416.         return -1;
  2417.     }
  2418.  
  2419.     if ((cp = strchr (component, '#')) != NULLCHAR)
  2420.         *cp = 0;
  2421.  
  2422.     /* send the request to the server */
  2423.     usprintf (s, "HEAD %s HTTP/1.0\nHost: %s\nUser-Agent: %s\n",
  2424.         component, Hostname, shortversion);
  2425.     usputs (s, "Accept-Language: en\nPragma: no-cache\n");
  2426.     usprintf (s, "Cache-Control: no-cache\nFrom: wwwuser@%s\n\n", Hostname);
  2427.  
  2428.     /* then while connection still open, store data into 'fp' */
  2429.     while ((c = recvchar (s)) != EOF)
  2430.         fputc (c, fp);
  2431.  
  2432.     close_s (s);
  2433.     rewind (fp);
  2434.  
  2435.     while (fgets (component, 128, fp))    {
  2436.         rip (component);
  2437.         if (!*component)
  2438.             break;
  2439.         if (content != NULLCHAR && !strncasecmp (component, "Content-type: ", 14))
  2440.             strcpy (content, &component[14]);
  2441.  
  2442.         if (location != NULLCHAR && !strncasecmp (component, "Location: ", 10))
  2443.             strcpy (location, &component[10]);
  2444.  
  2445.         if (modified != NULLCHAR && !strncasecmp (component, "Last-Modified: ", 15))
  2446.             strcpy (modified, &component[15]);
  2447.  
  2448.         /* else, bitbucket any other headers, for now */
  2449.     }
  2450.     (void) fclose (fp);
  2451.     return 1;
  2452. }
  2453.  
  2454.  
  2455.  
  2456. int
  2457. dobrowseremail (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
  2458. {
  2459. char buf[256];
  2460.  
  2461.     if (argc < 2)
  2462.         tprintf ("%s\n", BrowserEmail);
  2463.     else    {
  2464.         free (BrowserEmail);
  2465.         strcpy (buf, argv[1]);
  2466.         if (!strchr (buf, '@'))    {
  2467.             strcat (buf, "@");
  2468.             strcat (buf, Hostname);
  2469.         }
  2470.         BrowserEmail = strdup (buf);
  2471.     }
  2472.     return 0;
  2473. }
  2474.  
  2475.  
  2476.  
  2477. int
  2478. dobrowsercheck (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
  2479. {
  2480. int timeout;
  2481. int retval;
  2482. char modified[128], buf[256], *cp, *tname;
  2483. int doit, found;
  2484. FILE *fp, *fpout;
  2485. char *bptr;
  2486. char *bareurl, *origurl;
  2487.  
  2488.     if (argc != 3)    {
  2489.         tputs ("Usage: browsercheck <numseconds> <complete url>\n");
  2490.         return 1;
  2491.     }
  2492.  
  2493.     origurl = strdup (argv[2]);
  2494.     bareurl = strdup (origurl);
  2495.     cp = strchr (bareurl, '?');
  2496.     if (cp)
  2497.         *cp = 0;
  2498.     timeout = atoi (argv[1]);
  2499.     if (timeout)    {
  2500.         kalarm (timeout * 1000);
  2501.         retval = gethead (bareurl, NULLCHAR, modified, NULLCHAR);
  2502.         kalarm (0);
  2503.         if (retval == -1)
  2504.             tprintf ("Couldn't obtain url: %s\n", bareurl);
  2505.         else    {
  2506.             /* check for last update time */
  2507.             doit = 0;
  2508.             fp = fopen (CheckedURLs, READ_TEXT);
  2509.             if (fp != NULLFILE)    {
  2510.                 while (fgets (buf, 256, fp))    {
  2511.                     rip (buf);
  2512.                     cp = strchr (buf, ' ');
  2513.                     if (cp == NULLCHAR)
  2514.                         continue;
  2515.                     *cp++ = 0;
  2516.                     if (!strcasecmp (bareurl, buf))    {
  2517.                         if (!strcasecmp (cp, modified))
  2518.                             doit = 1;
  2519.                     }
  2520.                 }
  2521.                 (void) fclose (fp);
  2522.             }
  2523.             /* if doit != 0, it is new or newer */
  2524.             if (doit)    {
  2525.                 tprintf ("\nURL '%s' has NOT been updated!\n", bareurl);
  2526.                 return 0;
  2527.             }
  2528.             
  2529.             /* if newer, prompt (y/n/later) */
  2530.             if (!*modified)
  2531.                 tprintf ("\nURL '%s' has no\nmodification time/date stamp\nCannot tell if it has been updated.\nDisplay it? (Yes, No) [y/n] ", bareurl);
  2532.             else
  2533.                 tprintf ("\nURL '%s' has been updated.\nDisplay it? (Yes, No, or Later) [y/n/l] ", bareurl);
  2534.             tflush ();
  2535.             (void) recvline (Curproc->input, (unsigned char *) buf, 256);
  2536.             doit = tolower (*buf);
  2537.  
  2538.             if (doit == 'y' || doit == 'n')    {
  2539.                 /* update last update time */
  2540.                 tname = strdup (CheckedURLs);
  2541.                 tname[strlen(tname) - 1] = '_';
  2542.  
  2543.                 fp = fopen (CheckedURLs, READ_TEXT);
  2544.                 fpout = fopen (tname, WRITE_TEXT);
  2545.                 if (fpout != NULLFILE)    {
  2546.                     found = 0;
  2547.                     if (fp != NULLFILE)    {
  2548.                         while (fgets (buf, 256, fp))    {
  2549.                             cp = strchr (buf, ' ');
  2550.                             if (cp == NULLCHAR)
  2551.                                 continue;
  2552.                             *cp = 0;
  2553.                             if (!strcasecmp (bareurl, buf))    {
  2554.                                 strcpy (&cp[1], modified);
  2555.                                 strcat (&cp[1], "\n");
  2556.                                 found = 1;
  2557.                             }
  2558.                             *cp = ' ';
  2559.                             fputs (buf, fpout);
  2560.                         }
  2561.                         (void) fclose (fp);
  2562.                     }
  2563.                     if (!found)
  2564.                         fprintf (fpout, "%s %s\n", bareurl, modified);
  2565.                     (void) fclose (fpout);
  2566.                 }
  2567.                 (void) unlink (CheckedURLs);
  2568.                 (void) rename (tname, CheckedURLs);
  2569.                 free (tname);
  2570.  
  2571.                 if (doit == 'y')    {
  2572.                     sprintf (buf, "browser %s", origurl);
  2573.                     bptr = _variable_expansion (strdup (buf));
  2574.                     (void) cmdparse (Cmds, bptr, NULL);
  2575.                     free (bptr);
  2576.                 }
  2577.             }
  2578.         }
  2579.     }
  2580.     free (bareurl);
  2581.     free (origurl);
  2582.     return 0;
  2583. }
  2584.  
  2585.  
  2586.  
  2587. char *
  2588. obtain_statline_marquee (void)
  2589. {
  2590. struct browser *br;
  2591. char buf[200], *cp;
  2592. FILE *fp;
  2593.  
  2594.     br = (struct browser *) callocw (1, sizeof (struct browser));
  2595.     fp = tmpfile ();
  2596.     if (fp == NULLFILE || br == NULLBROWSER)
  2597.         return NULLCHAR;    /* shouldn't happen */
  2598.  
  2599.     log (-1, "Updating Statusline News Service from %s", HEADLINENewsHost);
  2600.     kalarm (120 * 1000);
  2601.     sprintf (buf, "http://%s/headline_news_retrieval_service", Hostname);
  2602.     if (http_url_to_file (br, buf, HEADLINENewsHost, 80, HEADLINENewsUrl, NULLCHAR, fp, NULLCHAR, 0, NULLCHAR, 1) == 0)
  2603.         parse_url (br, fp, 1);
  2604.     kalarm (0);
  2605.  
  2606.     (void) fclose (fp);
  2607.     cp = br->marquee;
  2608.     br->marquee = NULLCHAR;
  2609.     clear_browser (br);
  2610.     return (cp);
  2611. }
  2612.  
  2613.  
  2614.  
  2615. /* screen position is ALREADY set to the line desired */
  2616. static void
  2617. scroll_marquee (struct browser *br)
  2618. {
  2619.     if (br->marquee == NULLCHAR)
  2620.         return;
  2621.  
  2622.     goto_stat (br, 0);
  2623.     marquee_display (br->marquee, &br->marquee_position);
  2624. }
  2625.  
  2626.  
  2627.  
  2628. int
  2629. dobrowser (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
  2630. {
  2631. struct session *sp = NULLSESSION;
  2632. struct browser *br;
  2633. struct formlink *fl, *fl2;
  2634. int c, k;
  2635. int renderit = 1;
  2636. int done = 0;
  2637. int skipsession = 0;
  2638. char buf[128], newurl[256];
  2639. char *cp, *cp2;
  2640. int len;
  2641. struct url_links *lk;
  2642. int godown;
  2643.  
  2644.     /* Make sure this comes from console */
  2645.     if (Curproc->input != Command->input)
  2646.         return 0;
  2647.  
  2648.     if (!strncasecmp (argv[1], "telnet://", 9) ||
  2649.         !strncasecmp (argv[1], "ftp://", 6))
  2650.             skipsession = 1;
  2651.             
  2652.     /* Allocate a session descriptor */
  2653.     if (!skipsession)    {
  2654.         if ((sp = newsession ("browser", TELNET, 0)) == NULLSESSION) {
  2655.             tputs (TooManySessions);
  2656.             return 1;
  2657.         }
  2658.     }
  2659.  
  2660.     br = (struct browser *) callocw (1, sizeof (struct browser));
  2661.     br->current_screen_lines = SCREENlength - ((Current->screen->statline * 2) + 3);
  2662.     if (strstr (argv[1], "//"))
  2663.         strcpy (newurl, argv[1]);
  2664.     else
  2665.         sprintf (newurl, "http://%s", argv[1]);
  2666.     c = get_url (br, newurl, NULLCHAR, 0, NULLCHAR, 1);
  2667.     if (c == 1)    {
  2668.         if (!skipsession)
  2669.             freesession (sp);
  2670.         goto cleanup;
  2671.     }
  2672.     /* this SHOULDN'T ever happen... */
  2673.     if (sp == NULLSESSION)
  2674.         return 1;
  2675.  
  2676.     sp->ttystate.edit = sp->ttystate.echo = 0;
  2677.  
  2678.     /* real routine will call render_screen, then loop on input */
  2679.     while (!done)    {
  2680.         if (renderit)    {
  2681.             render_screen (br);
  2682.             renderit = 0;
  2683.         }
  2684.         kalarm (100);
  2685.         c = rrecvchar (Curproc->input);
  2686.         kalarm (0);
  2687.         if (c == -1)    {
  2688.             scroll_marquee (br);
  2689.             continue;
  2690.         }
  2691.         c = tolower (c);
  2692.  
  2693.         switch (c)    {
  2694.             case 'n':    /* move to next line of file */
  2695.                     if (br->current_top_line < (br->lines_in_url - 1))    {
  2696.                         br->current_top_line++;
  2697.                         renderit = 1;
  2698.                     }
  2699.                     break;
  2700.             case 'p':    /* move to previous line of file */
  2701.                     if (br->current_top_line)    {
  2702.                         br->current_top_line--;
  2703.                         renderit = 1;
  2704.                     }
  2705.                     break;
  2706.             case '\t':
  2707.             case 'd':    /* move forward one link in the file */
  2708. move_down:                if (br->current_link < (br->link_count - 1))    {
  2709.                         br->current_link++;
  2710.                         renderit = 1;
  2711.                         goto adj_check;
  2712.                     }
  2713.                     break;
  2714.             case 'u':    /* move back one link in the file */
  2715.                     if (br->current_link)    {
  2716.                         br->current_link--;
  2717.                         renderit = 1;
  2718. adj_check:                        /* also, adjust the screen window, if needed */
  2719.                         if (br->links[br->current_link].startline < br->current_top_line ||
  2720.                             br->links[br->current_link].endline >= br->current_top_line + br->current_screen_lines)
  2721.                             br->current_top_line = br->links[br->current_link].startline;
  2722.                     } else if (br->current_top_line)    {
  2723.                         br->current_top_line = 0;
  2724.                         renderit = 1;
  2725.                     }
  2726.                     break;
  2727.             case 'h':    /* display history linkbacks */
  2728.                     clrscr ();
  2729.                     send_blank_lines (3);
  2730.                     cputs ("\t\tLinkback History List");
  2731.                     send_blank_lines (2);
  2732.                     for (k = 0; k < br->total_in_linkback; k++)    {
  2733.                         cputs ("\t");
  2734.                         cputs ((k == br->current_linkback) ? "*" : " ");
  2735.                         cputs (br->linkback[k]);
  2736.                         cputs (Eol);
  2737.                     }
  2738.                     send_blank_lines (3);
  2739.                     cputs ("\t\tPress any key to return to the active page");
  2740.  
  2741.                     (void) rrecvchar (Curproc->input);
  2742.                     renderit = 1;
  2743.                     break;
  2744.             case '<':    /* shift screen left - only if already shifted right */
  2745.                     if (br->columnoffset)    {
  2746.                         br->columnoffset -= (SCREENwidth / 2);
  2747.                         if (br->columnoffset < 0)
  2748.                             br->columnoffset = 0;
  2749.                         renderit = 1;
  2750.                     }
  2751.                     break;
  2752.             case '>':    /* shift screen right - only if anymore to right */
  2753.                     if (br->columnoffset + (SCREENwidth / 2) < br->longestline - (SCREENwidth / 2))    {
  2754.                         br->columnoffset += (SCREENwidth / 2);
  2755.                         renderit = 1;
  2756.                     }
  2757.                     break;
  2758.             case '?':    /* do a help display for the user */
  2759.                     /* need to complete this */
  2760.                     clrscr ();
  2761.  
  2762.                     send_blank_lines (3);
  2763.                     cputs ("\t\t'n'\tMoves the screen to the next line");
  2764.                     cputs (Eol);
  2765.                     cputs ("\t\t'p'\tMoves the screen to the previous line");
  2766.                     cputs (Eol);
  2767.                     cputs ("\t\t' '\tMoves the screen down one page");
  2768.                     cputs (Eol);
  2769.                     cputs ("\t\t'-'\tMoves the screen up one page");
  2770.                     cputs (Eol);
  2771.                     cputs ("\t\t'>'\tShifts screen right one half page (if needed)");
  2772.                     cputs (Eol);
  2773.                     cputs ("\t\t'<'\tShifts screen left one half page (if needed)");
  2774.                     send_blank_lines (2);
  2775.  
  2776.                     cputs ("\t\t'd'\tActivates the next link down on page (<TAB> also)");
  2777.                     cputs (Eol);
  2778.                     cputs ("\t\t'u'\tActivates the next link up on page");
  2779.                     send_blank_lines (2);
  2780.  
  2781.                     cputs ("\t\t'g'\tGo to a new URL");
  2782.                     cputs (Eol);
  2783.                     cputs ("\t\t'r'\tReloads the current URL");
  2784.                     cputs (Eol);
  2785.                     cputs ("\t\t'h'\tDisplays the linkback list history");
  2786.                     cputs (Eol);
  2787.                     cputs ("\t\t'f'\tSelects the link one forward from here");
  2788.                     cputs (Eol);
  2789.                     cputs ("\t\t'b'\tSelects the link one backward from here");
  2790.                     send_blank_lines (2);
  2791.  
  2792.                     cputs ("\t\t's'\tSearches an index (if present on the page)");
  2793.                     send_blank_lines (2);
  2794.  
  2795.                     cputs ("\t\t'q'\tQuits the browser session");
  2796.                     cputs (Eol);
  2797.                     cputs ("\t\t'?'\tDisplays this help summary");
  2798.                     send_blank_lines (2);
  2799.  
  2800.                     cputs ("\t\t\t<CR> selects the active link or form component");
  2801.                     send_blank_lines (3);
  2802.  
  2803.                     cputs ("\t\tPress any key to return to the active page");
  2804.  
  2805.                     (void) rrecvchar (Curproc->input);
  2806.                     renderit = 1;
  2807.                     break;
  2808.             case 'q':    /* quit the browser */
  2809.                     done = 1;
  2810.                     break;
  2811.             case 's':    /* search the index - if present */
  2812.                     if (br->has_an_index)    {
  2813.                         blank_status (br);
  2814.                         Current->ttystate.edit = Current->ttystate.echo = 1;
  2815.                         goto_stat (br, 1);
  2816.                         cputs ("Enter Search String: ");
  2817.  
  2818.                         kwait (NULL);
  2819.                         (void) recvline (Curproc->input, (unsigned char *) buf, 128);
  2820.                         rip (buf);
  2821.  
  2822.                         Current->ttystate.edit = Current->ttystate.echo = 0;
  2823.                         sprintf (newurl, "%s?%s", br->current_url, buf);
  2824.                         (void) get_url (br, newurl, NULLCHAR, 0, NULLCHAR, 1);
  2825.                         renderit = 1;
  2826.                     }
  2827.                     break;
  2828.             case 'g':    /* go to an new url */
  2829.                     blank_status (br);
  2830.                     Current->ttystate.edit = Current->ttystate.echo = 1;
  2831.                     goto_stat (br, 0);
  2832.                     cputs ("Enter in a new URL in which to visit (if another site, start with '//')");
  2833.                     cputs (Eol);
  2834.                     cputs ("      Enter the complete URL: ");
  2835.  
  2836.                     kwait (NULL);
  2837.                     (void) recvline (Curproc->input, (unsigned char *) buf, 128);
  2838.                     rip (buf);
  2839.  
  2840.                     Current->ttystate.edit = Current->ttystate.echo = 0;
  2841.                     cp = rebuild_url (br, strdup (buf));
  2842.                     if (cp != NULLCHAR)
  2843.                         (void) get_url (br, cp, NULLCHAR, 0, NULLCHAR, 1);
  2844.                     free (cp);
  2845.                     renderit = 1;
  2846.                     break;
  2847.             case ' ':    /* down a page */
  2848.                     br->current_top_line += (SCREENLENGTH - (Current->screen->statline + 4));
  2849.                     if (br->current_top_line >= br->lines_in_url)
  2850.                         br->current_top_line = br->lines_in_url - 3;
  2851.                     renderit = 1;
  2852.                     break;
  2853.             case '-':    /* up a page */
  2854.                     br->current_top_line -= (SCREENLENGTH - (Current->screen->statline + 4));
  2855.                     if (br->current_top_line < 0)
  2856.                         br->current_top_line = 0;
  2857.                     renderit = 1;
  2858.                     break;
  2859.             case '\r':
  2860.             case '\n':    /* traverse the current link */
  2861.                     if (br->link_count == 0)
  2862.                         break;        /* no link to traverse */
  2863.                     lk = &br->links[br->current_link];
  2864.                     if (lk->url)    {
  2865.                         godown = 0;
  2866.                         (void) get_url (br, lk->url, NULLCHAR, 0, NULLCHAR, 1);
  2867.                     } else    {
  2868.                         if (lk->flink == NULLFLINK)
  2869.                             break;
  2870.  
  2871.                         /* must be a form component */
  2872.                         godown = 1;
  2873.                         switch (lk->flink->type)    {
  2874.                             case INPUT_CHECKBOX:
  2875.                                 lk->flink->bool ^= 1;
  2876.                                 br->display_lines[lk->flink->line][lk->flink->col] = (lk->flink->bool) ? 'X' : '_';
  2877.                                 break;
  2878.                             case INPUT_RADIO:
  2879.                                 if (lk->flink->bool)
  2880.                                     break;
  2881.                                 /* we need to insure that only this one radio button in this group is selected */
  2882.  
  2883.                                 for (fl = br->form->flink; fl != NULLFLINK; fl = fl->next)    {
  2884.                                     if (fl->type == INPUT_RADIO && !strcmp (fl->name, lk->flink->name))    {
  2885.                                         br->display_lines[fl->line][fl->col] = '_';
  2886.                                         fl->bool = 0;
  2887.                                     }
  2888.                                 }
  2889.                                 br->display_lines[lk->flink->line][lk->flink->col] = '*';
  2890.                                 lk->flink->bool = 1;
  2891.                                 break;
  2892.                             case INPUT_TEXT:
  2893.                             case INPUT_PASSWORD:
  2894.                                 blank_status (br);
  2895.                                 if (lk->flink->type == INPUT_TEXT)
  2896.                                     Current->ttystate.echo = 1;
  2897.                                 Current->ttystate.edit = 1;
  2898.                                 goto_stat (br, 1);
  2899.                                 cputs ("Enter Text String: ");
  2900.  
  2901.                                 kwait (NULL);
  2902.                                 k = recvline (Curproc->input, (unsigned char *) buf, (unsigned int) (lk->flink->size + 1));
  2903.                                 rip (buf);
  2904.  
  2905.                                 Current->ttystate.edit = Current->ttystate.echo = 0;
  2906.                                 if (!*buf)
  2907.                                     break;
  2908.                                 cp = &br->display_lines[lk->flink->line][lk->flink->col];
  2909.                                 for (len = 0; len < lk->flink->size; len++)
  2910.                                     cp[len] = '_';
  2911.                                 if (lk->flink->type == INPUT_TEXT)
  2912.                                     strncpy (cp, buf, strlen(buf));
  2913.                                 free (lk->flink->data);
  2914.                                 lk->flink->data = strdup (buf);
  2915.  
  2916.                                 /* if the user gave a string too long, then eat the rest of the line */
  2917.                                 if (k == lk->flink->size)
  2918.                                     while ((c = rrecvchar (Curproc->input)) != '\n')
  2919.                                         ;
  2920.                                 
  2921.                                 break;
  2922.                             case INPUT_RESET:
  2923.                                 for (fl = br->form->flink; fl != NULLFLINK; fl = fl->next)    {
  2924.                                     switch (fl->type)    {
  2925.                                         case INPUT_CHECKBOX:
  2926.                                             br->display_lines[fl->line][fl->col] = '_';
  2927.                                             fl->bool = 0;
  2928.                                             break;
  2929.                                         case INPUT_TEXT:
  2930.                                         case INPUT_PASSWORD:
  2931.                                             free (fl->data);
  2932.                                             fl->data = NULLCHAR;
  2933.                                             cp = &br->display_lines[fl->line][fl->col];
  2934.                                             for (len = 0; len < fl->size; len++)
  2935.                                                 cp[len] = '_';
  2936.                                             break;
  2937.                                         default:
  2938.                                             break;
  2939.                                     }
  2940.                                 }
  2941.                                 godown = 0;
  2942.                                 break;
  2943.                             case INPUT_SUBMIT:
  2944.                             case INPUT_IMAGE:
  2945.                                 /* formulate data to send in HTTP request */
  2946.                                 newurl[0] = 0;
  2947.  
  2948.                                 /* we start with the checkboxes, and handle them separately, since
  2949.                                    you can have several with the same name, and the values are to be
  2950.                                    concatenated together in one variable. */
  2951.                                 for (fl = br->form->flink; fl != NULLFLINK; fl = fl->next)
  2952.                                     if (fl->type == INPUT_CHECKBOX)
  2953.                                         fl->size = 0;    /* used to prevent repeats */
  2954.                                         
  2955.                                 for (fl = br->form->flink; fl != NULLFLINK; fl = fl->next)    {
  2956.                                     if (fl->type != INPUT_CHECKBOX || fl->size == 255)
  2957.                                         continue;
  2958.                                     cp = encode_post (fl->value);
  2959.                                     sprintf (buf, "%s%s=%s", (newurl[0]) ? "&" : "", fl->name, cp);
  2960.                                     free (cp);
  2961.                                     strcat (newurl, buf);
  2962.                                     fl->size = 255;
  2963.                                     for (fl2 = fl->next; fl2 != NULLFLINK; fl2 = fl2->next)    {
  2964.                                         if (fl2->type != INPUT_CHECKBOX || fl2->size == 255 || strcmp (fl->name, fl2->name))
  2965.                                             continue;
  2966.                                         cp = encode_post (fl2->value);
  2967.                                         strcat (newurl, ",");
  2968.                                         strcat (newurl, cp);
  2969.                                         free (cp);
  2970.                                         fl2->size = 255;
  2971.                                     }
  2972.                                 }
  2973.  
  2974.                                 /* now we handle everything else BUT the checkboxes. */
  2975.                                 for (fl = br->form->flink; fl != NULLFLINK; fl = fl->next)    {
  2976.                                     switch (fl->type)    {
  2977.                                         case INPUT_SUBMIT:
  2978.                                             if (fl->name == NULLCHAR)
  2979.                                                 continue;
  2980.                                             /* else, fall through */
  2981.                                         case INPUT_TEXT:
  2982.                                         case INPUT_PASSWORD:
  2983.                                             cp = encode_post (fl->data);
  2984.                                             sprintf (buf, "%s%s=%s", (newurl[0]) ? "&" : "", fl->name, cp);
  2985.                                             free (cp);
  2986.                                             break;
  2987.                                         case INPUT_HIDDEN:
  2988. add_to_request:                                        cp = encode_post (fl->value);
  2989.                                             sprintf (buf, "%s%s=%s", (newurl[0]) ? "&" : "", fl->name, cp);
  2990.                                             free (cp);
  2991.                                             break;
  2992.                                         case INPUT_RADIO:
  2993.                                             if (!fl->bool)
  2994.                                                 continue;
  2995.                                             goto add_to_request;
  2996.                                         default:
  2997.                                             continue;
  2998.                                     }
  2999.                                     strcat (newurl, buf);
  3000.                                 }
  3001.                                 /* send the post here */
  3002.                                 cp = br->form->action;
  3003.                                 br->form->action = NULLCHAR;
  3004.                                 cp2 = br->form->enctype;
  3005.                                 br->form->enctype = NULLCHAR;
  3006. #if 0
  3007. tcmdprintf ("Would post here: '%s'\n", cp);
  3008. #else
  3009.                                 (void) get_url (br, cp, newurl, br->form->method, cp2, 1);
  3010. #endif
  3011.                                 free (cp);
  3012.                                 free (cp2);
  3013.                                 godown = 0;
  3014.                                 break;
  3015.                             default:
  3016.                                 godown = 0;
  3017.                                 break;
  3018.                         }
  3019.                     }
  3020.                     renderit = 1;
  3021.                     if (godown)
  3022.                         goto move_down;
  3023.                     break;
  3024.             case 'f':    /* go forward one link */
  3025.                     if (br->current_linkback < (br->total_in_linkback - 1))    {
  3026.                         br->current_linkback++;
  3027.                         goto reload_link;
  3028.                     }
  3029.                     break;
  3030.             case 'b':    /* go back one link */
  3031.                     if (br->current_linkback)    {
  3032.                         br->current_linkback--;
  3033. reload_link:                    (void) get_url (br, br->linkback[br->current_linkback], NULLCHAR, 0, NULLCHAR, 0);
  3034.                         renderit = 1;
  3035.                     }
  3036.                     break;
  3037.             case 'r':    goto reload_link;
  3038.             case 0:
  3039.             case 1:
  3040.                     renderit = 1;
  3041.                     break;
  3042.             default:    break;
  3043.         }
  3044.         
  3045.     }
  3046.  
  3047.     close_s (sp->s);
  3048.     sp->s = -1;
  3049.  
  3050.     blank_status (br);
  3051.     goto_stat (br, 1);
  3052.     cputs ("                         ");
  3053.     (void) keywait (NULLCHAR, 1);
  3054.     freesession (sp);
  3055.     
  3056.     for (c = 0; c < br->total_in_linkback; c++)
  3057.         free (br->linkback[c]);
  3058. cleanup:
  3059.     while (br->myrealms)    {
  3060.         free (br->myrealms->host);
  3061.         free (br->myrealms->realm);
  3062.         free (br->myrealms->auth);
  3063.         br->myrealms = br->myrealms->next;
  3064.     }
  3065.     clear_browser (br);
  3066.     free (br);
  3067.     return 0;
  3068. }
  3069.  
  3070.  
  3071.  
  3072. #else
  3073. struct browser brow;
  3074.  
  3075.  
  3076.  
  3077. void
  3078. kwait (void *ignore)
  3079. {
  3080. }
  3081.  
  3082.  
  3083. char *
  3084. strlwr(s)
  3085. char *s;
  3086. {
  3087. register char *p = s;
  3088.  
  3089.     while (*p)
  3090.         *p = (char) tolower (*p), p++;
  3091.     return s;
  3092. }
  3093.  
  3094.  
  3095.  
  3096. void
  3097. rip(s)
  3098. register char *s;
  3099. {
  3100.     register char *cp;
  3101.  
  3102.     if((cp = strchr(s,'\n')) != NULLCHAR)
  3103.         *cp = '\0';
  3104. }
  3105.  
  3106.  
  3107.  
  3108. void
  3109. main (int argc, char *argv[])
  3110. {
  3111. FILE *fp;
  3112. int k;
  3113. struct formlink *fl;
  3114.  
  3115.     setbuf (stdout, NULL);
  3116.     brow.host = strdup ("www.lantz.com");
  3117.     brow.current_url = strdup ("/index.html");
  3118.     fp = fopen ("/tmp/index.html", "r");
  3119.     parse_url (&brow, fp, 0);
  3120.     (void) fclose (fp);
  3121.  
  3122.     printf ("Current URL: %s\nHost: %s\nPort: %d\nUrlLines: %d\n", brow.current_url,
  3123.         brow.host, brow.port, brow.lines_in_url);
  3124.     printf ("TopLine: %d\nTitle: %s\n\n", brow.current_top_line, brow.title);
  3125.     
  3126.     for (k = 0; k < brow.lines_in_url; k++)
  3127.         printf ("Line #%02d (%d) [%d]: %s\n", k,
  3128.             strlen (brow.display_lines[k]),
  3129.             brow.links_per_line[k], brow.display_lines[k]);
  3130.  
  3131.     printf ("\nLinkCount: %d\nCurrentLink: %d\n\n", brow.link_count, brow.current_link);
  3132.     for (k = 0; k < brow.link_count; k++)
  3133.         printf ("Link #%02d: startline=%d, startcol=%d, endline=%d, endcol=%d, url=%s\n",
  3134.             k, brow.links[k].startline, brow.links[k].startcolumn,
  3135.             brow.links[k].endline, brow.links[k].endcolumn,
  3136.             brow.links[k].url);
  3137.     if (brow.form != NULLFORM)    {
  3138.         printf ("\nForm active:\n\tURL: %s\n\tMethod: %d\n\tEnctype: %s\n\n",
  3139.             brow.form->action, brow.form->method, brow.form->enctype);
  3140.         for (fl = brow.form->flink; fl != NULLFLINK; fl = fl->next)
  3141.             printf ("Link:\tType: %d - line=%d, col=%d\tBool=%d - Data=%s\n"
  3142.                 "\tName=%s\tValue=%s\n\tAlign=%s - Size=%d - Maxlength=%d\n\n",
  3143.                 fl->type, fl->line, fl->col, fl->bool, fl->data,
  3144.                 fl->name, fl->value, fl->align, fl->size, fl->maxlength);
  3145.     }
  3146. }
  3147.  
  3148. #endif
  3149.  
  3150. #endif        /* BROWSER */
  3151.